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 static PropertyMap getInitialMap() { 46 return $nasgenmap$; 47 } 48 49 private ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength, final Global global) { 50 super(getInitialMap()); 51 checkConstructorArgs(buffer, byteOffset, elementLength); 52 this.setProto(getPrototype(global)); 53 this.setArray(factory().createArrayData(buffer, byteOffset, elementLength)); 54 } 55 56 ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) { 57 this(buffer, byteOffset, elementLength, Global.instance()); 58 } 59 60 private void checkConstructorArgs(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) { 61 if (byteOffset < 0 || elementLength < 0) { 62 throw new RuntimeException("byteOffset or length must not be negative"); 63 } 64 if (byteOffset + elementLength * bytesPerElement() > buffer.getByteLength()) { 65 throw new RuntimeException("byteOffset + byteLength out of range"); 66 } 67 if (byteOffset % bytesPerElement() != 0) { 68 throw new RuntimeException("byteOffset must be a multiple of the element size"); 69 } 70 } 71 72 private int bytesPerElement() { 73 return factory().bytesPerElement; 74 } 75 76 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 77 public static Object buffer(final Object self) { 78 return ((ArrayDataImpl)((ArrayBufferView)self).getArray()).buffer; 79 } 80 81 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 82 public static Object byteOffset(final Object self) { 83 return ((ArrayDataImpl)((ArrayBufferView)self).getArray()).byteOffset; 84 } 85 86 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 87 public static Object byteLength(final Object self) { 88 final ArrayBufferView view = (ArrayBufferView)self; 89 return ((ArrayDataImpl)view.getArray()).elementLength * view.bytesPerElement(); 90 } 91 92 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 93 public static Object length(final Object self) { 94 return ((ArrayBufferView)self).elementLength(); 95 } 96 97 @Override 98 public final Object getLength() { 99 return elementLength(); 100 } 101 102 private int elementLength() { 103 return ((ArrayDataImpl)getArray()).elementLength; 104 } 105 106 protected static abstract class ArrayDataImpl extends ArrayData { 107 protected final NativeArrayBuffer buffer; 108 protected final int byteOffset; 109 private final int elementLength; 110 111 protected ArrayDataImpl(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) { 112 super(elementLength); 113 this.buffer = buffer; 114 this.byteOffset = byteOffset; 115 this.elementLength = elementLength; 116 } 117 118 @Override 119 public ArrayData copy() { 120 throw new UnsupportedOperationException(); // Not used for ArrayBuffers 121 } 122 123 @Override 124 public Object[] asObjectArray() { 125 final Object[] array = new Object[elementLength]; 126 for (int i = 0; i < elementLength; i++) { 127 array[i] = getObjectImpl(i); 128 } 129 return array; 130 } 131 132 @Override 133 public ArrayData ensure(final long safeIndex) { 134 return this; 135 } 136 137 @Override 138 public void setLength(final long length) { 139 //empty? 140 //TODO is this right? 141 } 142 143 @Override 144 public ArrayData shrink(final long newLength) { 145 return this; 146 } 147 148 @Override 149 public ArrayData set(final int index, final Object value, final boolean strict) { 150 if (has(index)) { 151 setImpl(index, value); 152 } 153 return this; 154 } 155 156 @Override 157 public ArrayData set(final int index, final int value, final boolean strict) { 158 if (has(index)) { 159 setImpl(index, value); 160 } 161 return this; 162 } 163 164 @Override 165 public ArrayData set(final int index, final long value, final boolean strict) { 166 if (has(index)) { 167 setImpl(index, value); 168 } 169 return this; 170 } 171 172 @Override 173 public ArrayData set(final int index, final double value, final boolean strict) { 174 if (has(index)) { 175 setImpl(index, value); 176 } 177 return this; 178 } 179 180 @Override 181 public int getInt(final int index) { 182 return getIntImpl(index); 183 } 184 185 @Override 186 public long getLong(final int index) { 187 return getLongImpl(index); 188 } 189 190 @Override 191 public double getDouble(final int index) { 192 return getDoubleImpl(index); 193 } 194 195 @Override 196 public Object getObject(final int index) { 197 return getObjectImpl(index); 198 } 199 200 @Override 201 public boolean has(final int index) { 202 return index >= 0 && index < elementLength; 203 } 204 205 @Override 206 public boolean canDelete(final int index, final boolean strict) { 207 return false; 208 } 209 210 @Override 211 public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { 212 return false; 213 } 214 215 @Override 216 public ArrayData delete(final int index) { 217 return this; 218 } 219 220 @Override 221 public ArrayData delete(final long fromIndex, final long toIndex) { 222 return this; 223 } 224 225 @Override 226 protected ArrayData convert(final Class<?> type) { 227 return this; 228 } 229 230 @Override 231 public void shiftLeft(final int by) { 232 throw new UnsupportedOperationException(); 233 } 234 235 @Override 236 public ArrayData shiftRight(final int by) { 237 throw new UnsupportedOperationException(); 238 } 239 240 @Override 241 public Object pop() { 242 throw new UnsupportedOperationException(); 243 } 244 245 @Override 246 public ArrayData slice(final long from, final long to) { 247 throw new UnsupportedOperationException(); 248 } 249 250 protected abstract int getIntImpl(int key); 251 252 protected long getLongImpl(final int key) { 253 return getIntImpl(key); 254 } 255 256 protected double getDoubleImpl(final int key) { 257 return getIntImpl(key); 258 } 259 260 protected Object getObjectImpl(final int key) { 261 return getIntImpl(key); 262 } 263 264 protected abstract void setImpl(int key, int value); 265 266 protected void setImpl(final int key, final long value) { 267 setImpl(key, (int)value); 268 } 269 270 protected void setImpl(final int key, final double value) { 271 setImpl(key, JSType.toInt32(value)); 272 } 273 274 protected void setImpl(final int key, final Object value) { 275 setImpl(key, JSType.toInt32(value)); 276 } 277 278 protected abstract int byteIndex(int index); 279 } 280 281 protected static abstract class Factory { 282 final int bytesPerElement; 283 final int maxElementLength; 284 285 public Factory(final int bytesPerElement) { 286 this.bytesPerElement = bytesPerElement; 287 this.maxElementLength = Integer.MAX_VALUE / bytesPerElement; 288 } 289 290 public final ArrayBufferView construct(final int elementLength) { 291 if(elementLength > maxElementLength) { 292 throw rangeError("inappropriate.array.buffer.length", JSType.toString(elementLength)); 293 } 294 return construct(new NativeArrayBuffer(elementLength * bytesPerElement), 0, elementLength); 295 } 296 297 public abstract ArrayBufferView construct(NativeArrayBuffer buffer, int byteOffset, int elementLength); 298 299 public abstract ArrayData createArrayData(NativeArrayBuffer buffer, int byteOffset, int elementLength); 300 } 301 302 protected abstract Factory factory(); 303 304 protected abstract ScriptObject getPrototype(final Global global); 305 306 protected boolean isFloatArray() { 307 return false; 308 } 309 310 protected static ArrayBufferView constructorImpl(final Object[] args, final Factory factory) { 311 final Object arg0 = args.length != 0 ? args[0] : 0; 312 final ArrayBufferView dst; 313 final int length; 314 if (arg0 instanceof NativeArrayBuffer) { 315 // Constructor(ArrayBuffer buffer, optional unsigned long byteOffset, optional unsigned long length) 316 final NativeArrayBuffer buffer = (NativeArrayBuffer) arg0; 317 final int byteOffset = args.length > 1 ? JSType.toInt32(args[1]) : 0; 318 if (args.length > 2) { 319 length = JSType.toInt32(args[2]); 320 } else { 321 if ((buffer.getByteLength() - byteOffset) % factory.bytesPerElement != 0) { 322 throw new RuntimeException("buffer.byteLength - byteOffset must be a multiple of the element size"); 323 } 324 length = (buffer.getByteLength() - byteOffset) / factory.bytesPerElement; 325 } 326 return factory.construct(buffer, byteOffset, length); 327 } else if (arg0 instanceof ArrayBufferView) { 328 // Constructor(TypedArray array) 329 length = ((ArrayBufferView)arg0).elementLength(); 330 dst = factory.construct(length); 331 } else if (arg0 instanceof NativeArray) { 332 // Constructor(type[] array) 333 length = lengthToInt(((NativeArray) arg0).getArray().length()); 334 dst = factory.construct(length); 335 } else { 336 // Constructor(unsigned long length) 337 length = lengthToInt(JSType.toInt64(arg0)); 338 return factory.construct(length); 339 } 340 341 copyElements(dst, length, (ScriptObject)arg0, 0); 342 return dst; 343 } 344 345 protected static Object setImpl(final Object self, final Object array, final Object offset0) { 346 final ArrayBufferView dest = ((ArrayBufferView)self); 347 final int length; 348 if (array instanceof ArrayBufferView) { 349 // void set(TypedArray array, optional unsigned long offset) 350 length = ((ArrayBufferView)array).elementLength(); 351 } else if (array instanceof NativeArray) { 352 // void set(type[] array, optional unsigned long offset) 353 length = (int) (((NativeArray) array).getArray().length() & 0x7fff_ffff); 354 } else { 355 throw new RuntimeException("argument is not of array type"); 356 } 357 358 final ScriptObject source = (ScriptObject) array; 359 final int offset = JSType.toInt32(offset0); // default=0 360 361 if (dest.elementLength() < length + offset || offset < 0) { 362 throw new RuntimeException("offset or array length out of bounds"); 363 } 364 365 copyElements(dest, length, source, offset); 366 367 return ScriptRuntime.UNDEFINED; 368 } 369 370 private static void copyElements(final ArrayBufferView dest, final int length, final ScriptObject source, final int offset) { 371 if (!dest.isFloatArray()) { 372 for (int i = 0, j = offset; i < length; i++, j++) { 373 dest.set(j, source.getInt(i), false); 374 } 375 } else { 376 for (int i = 0, j = offset; i < length; i++, j++) { 377 dest.set(j, source.getDouble(i), false); 378 } 379 } 380 } 381 382 private static int lengthToInt(final long length) { 383 if (length > Integer.MAX_VALUE || length < 0) { 384 throw rangeError("inappropriate.array.buffer.length", JSType.toString(length)); 385 } 386 return (int) (length & Integer.MAX_VALUE); 387 } 388 389 protected static Object subarrayImpl(final Object self, final Object begin0, final Object end0) { 390 final ArrayBufferView arrayView = ((ArrayBufferView)self); 391 final int elementLength = arrayView.elementLength(); 392 final int begin = NativeArrayBuffer.adjustIndex(JSType.toInt32(begin0), elementLength); 393 final int end = NativeArrayBuffer.adjustIndex(end0 != ScriptRuntime.UNDEFINED ? JSType.toInt32(end0) : elementLength, elementLength); 394 final ArrayDataImpl arrayData = (ArrayDataImpl)arrayView.getArray(); 395 return arrayView.factory().construct(arrayData.buffer, arrayData.byteIndex(begin), Math.max(end - begin, 0)); 396 } 397 }