1 /*
   2  * Copyright (c) 2014, 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 package jdk.nashorn.internal.objects;
  26 
  27 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
  28 import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
  29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
  30 
  31 import java.nio.ByteBuffer;
  32 import java.nio.ByteOrder;
  33 import jdk.nashorn.internal.objects.annotations.Attribute;
  34 import jdk.nashorn.internal.objects.annotations.Constructor;
  35 import jdk.nashorn.internal.objects.annotations.Function;
  36 import jdk.nashorn.internal.objects.annotations.Property;
  37 import jdk.nashorn.internal.objects.annotations.ScriptClass;
  38 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
  39 import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
  40 import jdk.nashorn.internal.runtime.JSType;
  41 import jdk.nashorn.internal.runtime.PropertyMap;
  42 import jdk.nashorn.internal.runtime.ScriptObject;
  43 import jdk.nashorn.internal.runtime.ScriptRuntime;
  44 
  45 /**
  46  * <p>
  47  * DataView builtin constructor. Based on the specification here:
  48  * http://www.khronos.org/registry/typedarray/specs/latest/#8
  49  * </p>
  50  * <p>
  51  * An ArrayBuffer is a useful object for representing an arbitrary chunk of data.
  52  * In many cases, such data will be read from disk or from the network, and will
  53  * not follow the alignment restrictions that are imposed on the typed array views
  54  * described earlier. In addition, the data will often be heterogeneous in nature
  55  * and have a defined byte order. The DataView view provides a low-level interface
  56  * for reading such data from and writing it to an ArrayBuffer.
  57  * </p>
  58  * <p>
  59  * Regardless of the host computer's endianness, DataView reads or writes values
  60  * to or from main memory with a specified endianness: big or little.
  61  * </p>
  62  */
  63 @ScriptClass("DataView")
  64 public class NativeDataView extends ScriptObject {
  65     // initialized by nasgen
  66     private static PropertyMap $nasgenmap$;
  67 
  68     // inherited ArrayBufferView properties
  69 
  70     /**
  71      * Underlying ArrayBuffer storage object
  72      */
  73     @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT)
  74     public final Object buffer;
  75 
  76     /**
  77      * The offset in bytes from the start of the ArrayBuffer
  78      */
  79     @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT)
  80     public final int byteOffset;
  81 
  82     /**
  83      * The number of bytes from the offset that this DataView will reference
  84      */
  85     @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT)
  86     public final int byteLength;
  87 
  88     // underlying ByteBuffer
  89     private final ByteBuffer buf;
  90 
  91     private NativeDataView(NativeArrayBuffer arrBuf) {
  92         this(arrBuf, arrBuf.getBuffer(), 0);
  93     }
  94 
  95     private NativeDataView(NativeArrayBuffer arrBuf, int offset) {
  96         this(arrBuf, bufferFrom(arrBuf, offset), offset);
  97     }
  98 
  99     private NativeDataView(NativeArrayBuffer arrBuf, int offset, int length) {
 100         this(arrBuf, bufferFrom(arrBuf, offset, length), offset, length);
 101     }
 102 
 103     private NativeDataView(final NativeArrayBuffer arrBuf, final ByteBuffer buf, final int offset) {
 104        this(arrBuf, buf, offset, buf.capacity() - offset);
 105     }
 106 
 107     private NativeDataView(final NativeArrayBuffer arrBuf, final ByteBuffer buf, final int offset, final int length) {
 108         super(Global.instance().getDataViewPrototype(), $nasgenmap$);
 109         this.buffer = arrBuf;
 110         this.byteOffset = offset;
 111         this.byteLength = length;
 112         this.buf = buf;
 113     }
 114 
 115     /**
 116      * Create a new DataView object using the passed ArrayBuffer for its
 117      * storage. Optional byteOffset and byteLength can be used to limit the
 118      * section of the buffer referenced. The byteOffset indicates the offset in
 119      * bytes from the start of the ArrayBuffer, and the byteLength is the number
 120      * of bytes from the offset that this DataView will reference. If both
 121      * byteOffset and byteLength are omitted, the DataView spans the entire
 122      * ArrayBuffer range. If the byteLength is omitted, the DataView extends from
 123      * the given byteOffset until the end of the ArrayBuffer.
 124      *
 125      * If the given byteOffset and byteLength references an area beyond the end
 126      * of the ArrayBuffer an exception is raised.
 127 
 128      * @param newObj if this constructor was invoked with 'new' or not
 129      * @param self   constructor function object
 130      * @param args   arguments to the constructor
 131      * @return newly constructed DataView object
 132      */
 133     @Constructor(arity = 1)
 134     public static Object constructor(final boolean newObj, final Object self, final Object... args) {
 135         if (args.length == 0 || !(args[0] instanceof NativeArrayBuffer)) {
 136             throw typeError("not.an.arraybuffer.in.dataview");
 137         }
 138 
 139         final NativeArrayBuffer arrBuf = (NativeArrayBuffer) args[0];
 140         switch (args.length) {
 141             case 1:
 142                 return new NativeDataView(arrBuf);
 143             case 2:
 144                 return new NativeDataView(arrBuf, JSType.toInt32(args[1]));
 145             default:
 146                 return new NativeDataView(arrBuf, JSType.toInt32(args[1]), JSType.toInt32(args[2]));
 147         }
 148     }
 149 
 150     /**
 151      * Specialized version of DataView constructor
 152      *
 153      * @param newObj if this constructor was invoked with 'new' or not
 154      * @param self   constructor function object
 155      * @param arrBuf underlying ArrayBuffer storage object
 156      * @param offset offset in bytes from the start of the ArrayBuffer
 157      * @return newly constructed DataView object
 158      */
 159     @SpecializedConstructor
 160     public static Object constructor(final boolean newObj, final Object self, final Object arrBuf, final int offset) {
 161         if (!(arrBuf instanceof NativeArrayBuffer)) {
 162             throw typeError("not.an.arraybuffer.in.dataview");
 163         }
 164         return new NativeDataView((NativeArrayBuffer) arrBuf, offset);
 165     }
 166 
 167     /**
 168      * Specialized version of DataView constructor
 169      *
 170      * @param newObj if this constructor was invoked with 'new' or not
 171      * @param self   constructor function object
 172      * @param arrBuf underlying ArrayBuffer storage object
 173      * @param offset in bytes from the start of the ArrayBuffer
 174      * @param length is the number of bytes from the offset that this DataView will reference
 175      * @return newly constructed DataView object
 176      */
 177     @SpecializedConstructor
 178     public static Object constructor(final boolean newObj, final Object self, final Object arrBuf, final int offset, final int length) {
 179         if (!(arrBuf instanceof NativeArrayBuffer)) {
 180             throw typeError("not.an.arraybuffer.in.dataview");
 181         }
 182         return new NativeDataView((NativeArrayBuffer) arrBuf, offset, length);
 183     }
 184 
 185     // Gets the value of the given type at the specified byte offset
 186     // from the start of the view. There is no alignment constraint;
 187     // multi-byte values may be fetched from any offset.
 188     //
 189     // For multi-byte values, the optional littleEndian argument
 190     // indicates whether a big-endian or little-endian value should be
 191     // read. If false or undefined, a big-endian value is read.
 192     //
 193     // These methods raise an exception if they would read
 194     // beyond the end of the view.
 195 
 196     /**
 197      * Get 8-bit signed int from given byteOffset
 198      *
 199      * @param self DataView object
 200      * @param byteOffset byte offset to read from
 201      * @return 8-bit signed int value at the byteOffset
 202      */
 203     @Function(attributes = Attribute.NOT_ENUMERABLE)
 204     public static int getInt8(final Object self, final Object byteOffset) {
 205         try {
 206             return getBuffer(self).get(JSType.toInt32(byteOffset));
 207         } catch (final IndexOutOfBoundsException ioe) {
 208             throw rangeError(ioe, "dataview.offset");
 209         }
 210     }
 211 
 212     /**
 213      * Get 8-bit signed int from given byteOffset
 214      *
 215      * @param self DataView object
 216      * @param byteOffset byte offset to read from
 217      * @return 8-bit signed int value at the byteOffset
 218      */
 219     @SpecializedFunction
 220     public static int getInt8(final Object self, final int byteOffset) {
 221         try {
 222             return getBuffer(self).get(byteOffset);
 223         } catch (final IndexOutOfBoundsException ioe) {
 224             throw rangeError(ioe, "dataview.offset");
 225         }
 226     }
 227 
 228     /**
 229      * Get 8-bit unsigned int from given byteOffset
 230      *
 231      * @param self DataView object
 232      * @param byteOffset byte offset to read from
 233      * @return 8-bit unsigned int value at the byteOffset
 234      */
 235     @Function(attributes = Attribute.NOT_ENUMERABLE)
 236     public static int getUint8(final Object self, final Object byteOffset) {
 237         try {
 238             return (0xFF & getBuffer(self).get(JSType.toInt32(byteOffset)));
 239         } catch (final IndexOutOfBoundsException ioe) {
 240             throw rangeError(ioe, "dataview.offset");
 241         }
 242     }
 243 
 244     /**
 245      * Get 8-bit unsigned int from given byteOffset
 246      *
 247      * @param self DataView object
 248      * @param byteOffset byte offset to read from
 249      * @return 8-bit unsigned int value at the byteOffset
 250      */
 251     @SpecializedFunction
 252     public static int getUint8(final Object self, final int byteOffset) {
 253         try {
 254             return (0xFF & getBuffer(self).get(byteOffset));
 255         } catch (final IndexOutOfBoundsException ioe) {
 256             throw rangeError(ioe, "dataview.offset");
 257         }
 258     }
 259 
 260     /**
 261      * Get 16-bit signed int from given byteOffset
 262      *
 263      * @param self DataView object
 264      * @param byteOffset byte offset to read from
 265      * @param littleEndian (optional) flag indicating whether to read in little endian order
 266      * @return 16-bit signed int value at the byteOffset
 267      */
 268     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 269     public static int getInt16(final Object self, final Object byteOffset, final Object littleEndian) {
 270         try {
 271             return getBuffer(self, littleEndian).getShort(JSType.toInt32(byteOffset));
 272         } catch (final IndexOutOfBoundsException ioe) {
 273             throw rangeError(ioe, "dataview.offset");
 274         }
 275     }
 276 
 277     /**
 278      * Get 16-bit signed int from given byteOffset
 279      *
 280      * @param self DataView object
 281      * @param byteOffset byte offset to read from
 282      * @return 16-bit signed int value at the byteOffset
 283      */
 284     @SpecializedFunction
 285     public static int getInt16(final Object self, final int byteOffset) {
 286         try {
 287             return getBuffer(self, false).getShort(byteOffset);
 288         } catch (final IndexOutOfBoundsException ioe) {
 289             throw rangeError(ioe, "dataview.offset");
 290         }
 291     }
 292 
 293     /**
 294      * Get 16-bit signed int from given byteOffset
 295      *
 296      * @param self DataView object
 297      * @param byteOffset byte offset to read from
 298      * @param littleEndian (optional) flag indicating whether to read in little endian order
 299      * @return 16-bit signed int value at the byteOffset
 300      */
 301     @SpecializedFunction
 302     public static int getInt16(final Object self, final int byteOffset, final boolean littleEndian) {
 303         try {
 304             return getBuffer(self, littleEndian).getShort(byteOffset);
 305         } catch (final IndexOutOfBoundsException ioe) {
 306             throw rangeError(ioe, "dataview.offset");
 307         }
 308     }
 309 
 310     /**
 311      * Get 16-bit unsigned int from given byteOffset
 312      *
 313      * @param self DataView object
 314      * @param byteOffset byte offset to read from
 315      * @param littleEndian (optional) flag indicating whether to read in little endian order
 316      * @return 16-bit unsigned int value at the byteOffset
 317      */
 318     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 319     public static int getUint16(final Object self, final Object byteOffset, final Object littleEndian) {
 320         try {
 321             return (int) (0xFFFF & getBuffer(self, littleEndian).getShort(JSType.toInt32(byteOffset)));
 322         } catch (final IndexOutOfBoundsException ioe) {
 323             throw rangeError(ioe, "dataview.offset");
 324         }
 325     }
 326 
 327     /**
 328      * Get 16-bit unsigned int from given byteOffset
 329      *
 330      * @param self DataView object
 331      * @param byteOffset byte offset to read from
 332      * @return 16-bit unsigned int value at the byteOffset
 333      */
 334     @SpecializedFunction
 335     public static int getUint16(final Object self, final int byteOffset) {
 336         try {
 337             return (int) (0xFFFF & getBuffer(self, false).getShort(byteOffset));
 338         } catch (final IndexOutOfBoundsException ioe) {
 339             throw rangeError(ioe, "dataview.offset");
 340         }
 341     }
 342 
 343     /**
 344      * Get 16-bit unsigned int from given byteOffset
 345      *
 346      * @param self DataView object
 347      * @param byteOffset byte offset to read from
 348      * @param littleEndian (optional) flag indicating whether to read in little endian order
 349      * @return 16-bit unsigned int value at the byteOffset
 350      */
 351     @SpecializedFunction
 352     public static int getUint16(final Object self, final int byteOffset, final boolean littleEndian) {
 353         try {
 354             return (int) (0xFFFF & getBuffer(self, littleEndian).getShort(byteOffset));
 355         } catch (final IndexOutOfBoundsException ioe) {
 356             throw rangeError(ioe, "dataview.offset");
 357         }
 358     }
 359 
 360     /**
 361      * Get 32-bit signed int from given byteOffset
 362      *
 363      * @param self DataView object
 364      * @param byteOffset byte offset to read from
 365      * @param littleEndian (optional) flag indicating whether to read in little endian order
 366      * @return 32-bit signed int value at the byteOffset
 367      */
 368     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 369     public static int getInt32(final Object self, final Object byteOffset, final Object littleEndian) {
 370         try {
 371             return getBuffer(self, littleEndian).getInt(JSType.toInt32(byteOffset));
 372         } catch (final IndexOutOfBoundsException ioe) {
 373             throw rangeError(ioe, "dataview.offset");
 374         }
 375     }
 376 
 377     /**
 378      * Get 32-bit signed int from given byteOffset
 379      *
 380      * @param self DataView object
 381      * @param byteOffset byte offset to read from
 382      * @return 32-bit signed int value at the byteOffset
 383      */
 384     @SpecializedFunction
 385     public static int getInt32(final Object self, final int byteOffset) {
 386         try {
 387             return getBuffer(self, false).getInt(byteOffset);
 388         } catch (final IndexOutOfBoundsException ioe) {
 389             throw rangeError(ioe, "dataview.offset");
 390         }
 391     }
 392 
 393     /**
 394      * Get 32-bit signed int from given byteOffset
 395      *
 396      * @param self DataView object
 397      * @param byteOffset byte offset to read from
 398      * @param littleEndian (optional) flag indicating whether to read in little endian order
 399      * @return 32-bit signed int value at the byteOffset
 400      */
 401     @SpecializedFunction
 402     public static int getInt32(final Object self, final int byteOffset, final boolean littleEndian) {
 403         try {
 404             return getBuffer(self, littleEndian).getInt(byteOffset);
 405         } catch (final IndexOutOfBoundsException ioe) {
 406             throw rangeError(ioe, "dataview.offset");
 407         }
 408     }
 409 
 410     /**
 411      * Get 32-bit unsigned int from given byteOffset
 412      *
 413      * @param self DataView object
 414      * @param byteOffset byte offset to read from
 415      * @param littleEndian (optional) flag indicating whether to read in little endian order
 416      * @return 32-bit unsigned int value at the byteOffset
 417      */
 418     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 419     public static long getUint32(final Object self, final Object byteOffset, final Object littleEndian) {
 420         try {
 421             return (long) (0xFFFFFFFFL & getBuffer(self, littleEndian).getInt(JSType.toInt32(byteOffset)));
 422         } catch (final IndexOutOfBoundsException ioe) {
 423             throw rangeError(ioe, "dataview.offset");
 424         }
 425     }
 426 
 427     /**
 428      * Get 32-bit unsigned int from given byteOffset
 429      *
 430      * @param self DataView object
 431      * @param byteOffset byte offset to read from
 432      * @return 32-bit unsigned int value at the byteOffset
 433      */
 434     @SpecializedFunction
 435     public static long getUint32(final Object self, final int byteOffset) {
 436         try {
 437             return (long) (0xFFFFFFFFL & getBuffer(self, false).getInt(JSType.toInt32(byteOffset)));
 438         } catch (final IndexOutOfBoundsException ioe) {
 439             throw rangeError(ioe, "dataview.offset");
 440         }
 441     }
 442 
 443     /**
 444      * Get 32-bit unsigned int from given byteOffset
 445      *
 446      * @param self DataView object
 447      * @param byteOffset byte offset to read from
 448      * @param littleEndian (optional) flag indicating whether to read in little endian order
 449      * @return 32-bit unsigned int value at the byteOffset
 450      */
 451     @SpecializedFunction
 452     public static long getUint32(final Object self, final int byteOffset, final boolean littleEndian) {
 453         try {
 454             return (long) (0xFFFFFFFFL & getBuffer(self, littleEndian).getInt(JSType.toInt32(byteOffset)));
 455         } catch (final IndexOutOfBoundsException ioe) {
 456             throw rangeError(ioe, "dataview.offset");
 457         }
 458     }
 459 
 460     /**
 461      * Get 32-bit float value from given byteOffset
 462      *
 463      * @param self DataView object
 464      * @param byteOffset byte offset to read from
 465      * @param littleEndian (optional) flag indicating whether to read in little endian order
 466      * @return 32-bit float value at the byteOffset
 467      */
 468     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 469     public static double getFloat32(final Object self, final Object byteOffset, final Object littleEndian) {
 470         try {
 471             return getBuffer(self, littleEndian).getFloat(JSType.toInt32(byteOffset));
 472         } catch (final IndexOutOfBoundsException ioe) {
 473             throw rangeError(ioe, "dataview.offset");
 474         }
 475     }
 476 
 477     /**
 478      * Get 32-bit float value from given byteOffset
 479      *
 480      * @param self DataView object
 481      * @param byteOffset byte offset to read from
 482      * @return 32-bit float value at the byteOffset
 483      */
 484     @SpecializedFunction
 485     public static double getFloat32(final Object self, final int byteOffset) {
 486         try {
 487             return getBuffer(self, false).getFloat(byteOffset);
 488         } catch (final IndexOutOfBoundsException ioe) {
 489             throw rangeError(ioe, "dataview.offset");
 490         }
 491     }
 492 
 493     /**
 494      * Get 32-bit float value from given byteOffset
 495      *
 496      * @param self DataView object
 497      * @param byteOffset byte offset to read from
 498      * @param littleEndian (optional) flag indicating whether to read in little endian order
 499      * @return 32-bit float value at the byteOffset
 500      */
 501     @SpecializedFunction
 502     public static double getFloat32(final Object self, final int byteOffset, final boolean littleEndian) {
 503         try {
 504             return getBuffer(self, littleEndian).getFloat(byteOffset);
 505         } catch (final IndexOutOfBoundsException ioe) {
 506             throw rangeError(ioe, "dataview.offset");
 507         }
 508     }
 509 
 510     /**
 511      * Get 64-bit float value from given byteOffset
 512      *
 513      * @param self DataView object
 514      * @param byteOffset byte offset to read from
 515      * @param littleEndian (optional) flag indicating whether to read in little endian order
 516      * @return 64-bit float value at the byteOffset
 517      */
 518     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 519     public static double getFloat64(final Object self, final Object byteOffset, final Object littleEndian) {
 520         try {
 521             return getBuffer(self, littleEndian).getDouble(JSType.toInt32(byteOffset));
 522         } catch (final IndexOutOfBoundsException ioe) {
 523             throw rangeError(ioe, "dataview.offset");
 524         }
 525     }
 526 
 527     /**
 528      * Get 64-bit float value from given byteOffset
 529      *
 530      * @param self DataView object
 531      * @param byteOffset byte offset to read from
 532      * @return 64-bit float value at the byteOffset
 533      */
 534     @SpecializedFunction
 535     public static double getFloat64(final Object self, final int byteOffset) {
 536         try {
 537             return getBuffer(self, false).getDouble(byteOffset);
 538         } catch (final IndexOutOfBoundsException ioe) {
 539             throw rangeError(ioe, "dataview.offset");
 540         }
 541     }
 542 
 543     /**
 544      * Get 64-bit float value from given byteOffset
 545      *
 546      * @param self DataView object
 547      * @param byteOffset byte offset to read from
 548      * @param littleEndian (optional) flag indicating whether to read in little endian order
 549      * @return 64-bit float value at the byteOffset
 550      */
 551     @SpecializedFunction
 552     public static double getFloat64(final Object self, final int byteOffset, final boolean littleEndian) {
 553         try {
 554             return getBuffer(self, littleEndian).getDouble(byteOffset);
 555         } catch (final IndexOutOfBoundsException ioe) {
 556             throw rangeError(ioe, "dataview.offset");
 557         }
 558     }
 559 
 560     // Stores a value of the given type at the specified byte offset
 561     // from the start of the view. There is no alignment constraint;
 562     // multi-byte values may be stored at any offset.
 563     //
 564     // For multi-byte values, the optional littleEndian argument
 565     // indicates whether the value should be stored in big-endian or
 566     // little-endian byte order. If false or undefined, the value is
 567     // stored in big-endian byte order.
 568     //
 569     // These methods raise an exception if they would write
 570     // beyond the end of the view.
 571 
 572     /**
 573      * Set 8-bit signed int at the given byteOffset
 574      *
 575      * @param self DataView object
 576      * @param byteOffset byte offset to read from
 577      * @param value byte value to set
 578      * @return undefined
 579      */
 580     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 581     public static Object setInt8(final Object self, final Object byteOffset, final Object value) {
 582         try {
 583             getBuffer(self).put(JSType.toInt32(byteOffset), (byte)JSType.toInt32(value));
 584             return UNDEFINED;
 585         } catch (final IndexOutOfBoundsException ioe) {
 586             throw rangeError(ioe, "dataview.offset");
 587         }
 588     }
 589 
 590     /**
 591      * Set 8-bit signed int at the given byteOffset
 592      *
 593      * @param self DataView object
 594      * @param byteOffset byte offset to read from
 595      * @param value byte value to set
 596      * @return undefined
 597      */
 598     @SpecializedFunction
 599     public static Object setInt8(final Object self, final int byteOffset, final int value) {
 600         try {
 601             getBuffer(self).put(byteOffset, (byte)value);
 602             return UNDEFINED;
 603         } catch (final IndexOutOfBoundsException ioe) {
 604             throw rangeError(ioe, "dataview.offset");
 605         }
 606     }
 607 
 608     /**
 609      * Set 8-bit unsigned int at the given byteOffset
 610      *
 611      * @param self DataView object
 612      * @param byteOffset byte offset to write at
 613      * @param value byte value to set
 614      * @return undefined
 615      */
 616     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 617     public static Object setUint8(final Object self, final Object byteOffset, final Object value) {
 618         try {
 619             getBuffer(self).put(JSType.toInt32(byteOffset), (byte)JSType.toInt32(value));
 620             return UNDEFINED;
 621         } catch (final IndexOutOfBoundsException ioe) {
 622             throw rangeError(ioe, "dataview.offset");
 623         }
 624     }
 625 
 626     /**
 627      * Set 8-bit unsigned int at the given byteOffset
 628      *
 629      * @param self DataView object
 630      * @param byteOffset byte offset to write at
 631      * @param value byte value to set
 632      * @return undefined
 633      */
 634     @SpecializedFunction
 635     public static Object setUint8(final Object self, final int byteOffset, final int value) {
 636         try {
 637             getBuffer(self).put(byteOffset, (byte)value);
 638             return UNDEFINED;
 639         } catch (final IndexOutOfBoundsException ioe) {
 640             throw rangeError(ioe, "dataview.offset");
 641         }
 642     }
 643 
 644     /**
 645      * Set 16-bit signed int at the given byteOffset
 646      *
 647      * @param self DataView object
 648      * @param byteOffset byte offset to write at
 649      * @param value short value to set
 650      * @param littleEndian (optional) flag indicating whether to write in little endian order
 651      * @return undefined
 652      */
 653     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 654     public static Object setInt16(final Object self, final Object byteOffset, final Object value, final Object littleEndian) {
 655         try {
 656             getBuffer(self, littleEndian).putShort(JSType.toInt32(byteOffset), (short)JSType.toInt32(value));
 657             return UNDEFINED;
 658         } catch (final IndexOutOfBoundsException ioe) {
 659             throw rangeError(ioe, "dataview.offset");
 660         }
 661     }
 662 
 663     /**
 664      * Set 16-bit signed int at the given byteOffset
 665      *
 666      * @param self DataView object
 667      * @param byteOffset byte offset to write at
 668      * @param value short value to set
 669      * @return undefined
 670      */
 671     @SpecializedFunction
 672     public static Object setInt16(final Object self, final int byteOffset, final int value) {
 673         try {
 674             getBuffer(self, false).putShort(byteOffset, (short)value);
 675             return UNDEFINED;
 676         } catch (final IndexOutOfBoundsException ioe) {
 677             throw rangeError(ioe, "dataview.offset");
 678         }
 679     }
 680 
 681     /**
 682      * Set 16-bit signed int at the given byteOffset
 683      *
 684      * @param self DataView object
 685      * @param byteOffset byte offset to write at
 686      * @param value short value to set
 687      * @param littleEndian (optional) flag indicating whether to write in little endian order
 688      * @return undefined
 689      */
 690     @SpecializedFunction
 691     public static Object setInt16(final Object self, final int byteOffset, final int value, final boolean littleEndian) {
 692         try {
 693             getBuffer(self, littleEndian).putShort(byteOffset, (short)value);
 694             return UNDEFINED;
 695         } catch (final IndexOutOfBoundsException ioe) {
 696             throw rangeError(ioe, "dataview.offset");
 697         }
 698     }
 699 
 700     /**
 701      * Set 16-bit unsigned int at the given byteOffset
 702      *
 703      * @param self DataView object
 704      * @param byteOffset byte offset to write at
 705      * @param value short value to set
 706      * @param littleEndian (optional) flag indicating whether to write in little endian order
 707      * @return undefined
 708      */
 709     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 710     public static Object setUint16(final Object self, final Object byteOffset, final Object value, final Object littleEndian) {
 711         try {
 712             getBuffer(self, littleEndian).putShort(JSType.toInt32(byteOffset), (short)JSType.toInt32(value));
 713             return UNDEFINED;
 714         } catch (final IndexOutOfBoundsException ioe) {
 715             throw rangeError(ioe, "dataview.offset");
 716         }
 717     }
 718 
 719     /**
 720      * Set 16-bit unsigned int at the given byteOffset
 721      *
 722      * @param self DataView object
 723      * @param byteOffset byte offset to write at
 724      * @param value short value to set
 725      * @return undefined
 726      */
 727     @SpecializedFunction
 728     public static Object setUint16(final Object self, final int byteOffset, final int value) {
 729         try {
 730             getBuffer(self, false).putShort(byteOffset, (short)value);
 731             return UNDEFINED;
 732         } catch (final IndexOutOfBoundsException ioe) {
 733             throw rangeError(ioe, "dataview.offset");
 734         }
 735     }
 736 
 737     /**
 738      * Set 16-bit unsigned int at the given byteOffset
 739      *
 740      * @param self DataView object
 741      * @param byteOffset byte offset to write at
 742      * @param value short value to set
 743      * @param littleEndian (optional) flag indicating whether to write in little endian order
 744      * @return undefined
 745      */
 746     @SpecializedFunction
 747     public static Object setUint16(final Object self, final int byteOffset, final int value, final boolean littleEndian) {
 748         try {
 749             getBuffer(self, littleEndian).putShort(byteOffset, (short)value);
 750             return UNDEFINED;
 751         } catch (final IndexOutOfBoundsException ioe) {
 752             throw rangeError(ioe, "dataview.offset");
 753         }
 754     }
 755 
 756     /**
 757      * Set 32-bit signed int at the given byteOffset
 758      *
 759      * @param self DataView object
 760      * @param byteOffset byte offset to write at
 761      * @param value int value to set
 762      * @param littleEndian (optional) flag indicating whether to write in little endian order
 763      * @return undefined
 764      */
 765     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 766     public static Object setInt32(final Object self, final Object byteOffset, final Object value, final Object littleEndian) {
 767         try {
 768             getBuffer(self, littleEndian).putInt(JSType.toInt32(byteOffset), (int)JSType.toInt32(value));
 769             return UNDEFINED;
 770         } catch (final IndexOutOfBoundsException ioe) {
 771             throw rangeError(ioe, "dataview.offset");
 772         }
 773     }
 774 
 775     /**
 776      * Set 32-bit signed int at the given byteOffset
 777      *
 778      * @param self DataView object
 779      * @param byteOffset byte offset to write at
 780      * @param value int value to set
 781      * @return undefined
 782      */
 783     @SpecializedFunction
 784     public static Object setInt32(final Object self, final int byteOffset, final int value) {
 785         try {
 786             getBuffer(self, false).putInt(byteOffset, value);
 787             return UNDEFINED;
 788         } catch (final IndexOutOfBoundsException ioe) {
 789             throw rangeError(ioe, "dataview.offset");
 790         }
 791     }
 792 
 793     /**
 794      * Set 32-bit signed int at the given byteOffset
 795      *
 796      * @param self DataView object
 797      * @param byteOffset byte offset to write at
 798      * @param value int value to set
 799      * @param littleEndian (optional) flag indicating whether to write in little endian order
 800      * @return undefined
 801      */
 802     @SpecializedFunction
 803     public static Object setInt32(final Object self, final int byteOffset, final int value, final boolean littleEndian) {
 804         try {
 805             getBuffer(self, littleEndian).putInt(byteOffset, value);
 806             return UNDEFINED;
 807         } catch (final IndexOutOfBoundsException ioe) {
 808             throw rangeError(ioe, "dataview.offset");
 809         }
 810     }
 811 
 812     /**
 813      * Set 32-bit unsigned int at the given byteOffset
 814      *
 815      * @param self DataView object
 816      * @param byteOffset byte offset to write at
 817      * @param value int value to set
 818      * @param littleEndian (optional) flag indicating whether to write in little endian order
 819      * @return undefined
 820      */
 821     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 822     public static Object setUint32(final Object self, final Object byteOffset, final Object value, final Object littleEndian) {
 823         try {
 824             getBuffer(self, littleEndian).putInt(JSType.toInt32(byteOffset), (int)JSType.toUint32(value));
 825             return UNDEFINED;
 826         } catch (final IndexOutOfBoundsException ioe) {
 827             throw rangeError(ioe, "dataview.offset");
 828         }
 829     }
 830 
 831     /**
 832      * Set 32-bit unsigned int at the given byteOffset
 833      *
 834      * @param self DataView object
 835      * @param byteOffset byte offset to write at
 836      * @param value int value to set
 837      * @return undefined
 838      */
 839     @SpecializedFunction
 840     public static Object setUint32(final Object self, final int byteOffset, final long value) {
 841         try {
 842             getBuffer(self, false).putInt(byteOffset, (int)value);
 843             return UNDEFINED;
 844         } catch (final IndexOutOfBoundsException ioe) {
 845             throw rangeError(ioe, "dataview.offset");
 846         }
 847     }
 848 
 849     /**
 850      * Set 32-bit unsigned int at the given byteOffset
 851      *
 852      * @param self DataView object
 853      * @param byteOffset byte offset to write at
 854      * @param value int value to set
 855      * @param littleEndian (optional) flag indicating whether to write in little endian order
 856      * @return undefined
 857      */
 858     @SpecializedFunction
 859     public static Object setUint32(final Object self, final int byteOffset, final long value, final boolean littleEndian) {
 860         try {
 861             getBuffer(self, littleEndian).putInt(byteOffset, (int)value);
 862             return UNDEFINED;
 863         } catch (final IndexOutOfBoundsException ioe) {
 864             throw rangeError(ioe, "dataview.offset");
 865         }
 866     }
 867 
 868     /**
 869      * Set 32-bit float at the given byteOffset
 870      *
 871      * @param self DataView object
 872      * @param byteOffset byte offset to write at
 873      * @param value float value to set
 874      * @param littleEndian (optional) flag indicating whether to write in little endian order
 875      * @return undefined
 876      */
 877     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 878     public static Object setFloat32(final Object self, final Object byteOffset, final Object value, final Object littleEndian) {
 879         try {
 880             getBuffer(self, littleEndian).putFloat((int)JSType.toUint32(byteOffset), (float)JSType.toNumber(value));
 881             return UNDEFINED;
 882         } catch (final IndexOutOfBoundsException ioe) {
 883             throw rangeError(ioe, "dataview.offset");
 884         }
 885     }
 886 
 887     /**
 888      * Set 32-bit float at the given byteOffset
 889      *
 890      * @param self DataView object
 891      * @param byteOffset byte offset to write at
 892      * @param value float value to set
 893      * @return undefined
 894      */
 895     @SpecializedFunction
 896     public static Object setFloat32(final Object self, final int byteOffset, final double value) {
 897         try {
 898             getBuffer(self, false).putFloat(byteOffset, (float)value);
 899             return UNDEFINED;
 900         } catch (final IndexOutOfBoundsException ioe) {
 901             throw rangeError(ioe, "dataview.offset");
 902         }
 903     }
 904 
 905     /**
 906      * Set 32-bit float at the given byteOffset
 907      *
 908      * @param self DataView object
 909      * @param byteOffset byte offset to write at
 910      * @param value float value to set
 911      * @param littleEndian (optional) flag indicating whether to write in little endian order
 912      * @return undefined
 913      */
 914     @SpecializedFunction
 915     public static Object setFloat32(final Object self, final int byteOffset, final double value, final boolean littleEndian) {
 916         try {
 917             getBuffer(self, littleEndian).putFloat(byteOffset, (float)value);
 918             return UNDEFINED;
 919         } catch (final IndexOutOfBoundsException ioe) {
 920             throw rangeError(ioe, "dataview.offset");
 921         }
 922     }
 923 
 924     /**
 925      * Set 64-bit float at the given byteOffset
 926      *
 927      * @param self DataView object
 928      * @param byteOffset byte offset to write at
 929      * @param value double value to set
 930      * @param littleEndian (optional) flag indicating whether to write in little endian order
 931      * @return undefined
 932      */
 933     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 934     public static Object setFloat64(final Object self, final Object byteOffset, final Object value, final Object littleEndian) {
 935         try {
 936             getBuffer(self, littleEndian).putDouble((int)JSType.toUint32(byteOffset), JSType.toNumber(value));
 937             return UNDEFINED;
 938         } catch (final IndexOutOfBoundsException ioe) {
 939             throw rangeError(ioe, "dataview.offset");
 940         }
 941     }
 942 
 943     /**
 944      * Set 64-bit float at the given byteOffset
 945      *
 946      * @param self DataView object
 947      * @param byteOffset byte offset to write at
 948      * @param value double value to set
 949      * @return undefined
 950      */
 951     @SpecializedFunction
 952     public static Object setFloat64(final Object self, final int byteOffset, final double value) {
 953         try {
 954             getBuffer(self, false).putDouble(byteOffset, value);
 955             return UNDEFINED;
 956         } catch (final IndexOutOfBoundsException ioe) {
 957             throw rangeError(ioe, "dataview.offset");
 958         }
 959     }
 960 
 961     /**
 962      * Set 64-bit float at the given byteOffset
 963      *
 964      * @param self DataView object
 965      * @param byteOffset byte offset to write at
 966      * @param value double value to set
 967      * @param littleEndian (optional) flag indicating whether to write in little endian order
 968      * @return undefined
 969      */
 970     @SpecializedFunction
 971     public static Object setFloat64(final Object self, final int byteOffset, final double value, final boolean littleEndian) {
 972         try {
 973             getBuffer(self, littleEndian).putDouble(byteOffset, value);
 974             return UNDEFINED;
 975         } catch (final IndexOutOfBoundsException ioe) {
 976             throw rangeError(ioe, "dataview.offset");
 977         }
 978     }
 979 
 980     // internals only below this point
 981     private static ByteBuffer bufferFrom(final NativeArrayBuffer nab, final int offset) {
 982         try {
 983             return nab.getBuffer(offset);
 984         } catch (final IndexOutOfBoundsException ioe) {
 985             throw rangeError(ioe, "dataview.constructor.offset");
 986         }
 987     }
 988 
 989     private static ByteBuffer bufferFrom(final NativeArrayBuffer nab, final int offset, final int length) {
 990         try {
 991             return nab.getBuffer(offset, length);
 992         } catch (final IndexOutOfBoundsException ioe) {
 993             throw rangeError(ioe, "dataview.constructor.offset");
 994         }
 995     }
 996 
 997     private static NativeDataView checkSelf(final Object self) {
 998         if (!(self instanceof NativeDataView)) {
 999             throw typeError("not.an.arraybuffer", ScriptRuntime.safeToString(self));
1000         }
1001         return (NativeDataView)self;
1002     }
1003 
1004     private static ByteBuffer getBuffer(final Object self) {
1005         return checkSelf(self).buf;
1006     }
1007 
1008     private static ByteBuffer getBuffer(final Object self, final Object littleEndian) {
1009         return getBuffer(self, JSType.toBoolean(littleEndian));
1010     }
1011 
1012     private static ByteBuffer getBuffer(final Object self, final boolean littleEndian) {
1013         return getBuffer(self).order(littleEndian? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
1014     }
1015 }