ArrayBufferView.java revision 1036:f0b5e3900a10
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 26package jdk.nashorn.internal.objects; 27 28import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; 29import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 30import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 31 32import java.nio.ByteBuffer; 33import java.nio.ByteOrder; 34 35import jdk.internal.dynalink.CallSiteDescriptor; 36import jdk.internal.dynalink.linker.GuardedInvocation; 37import jdk.internal.dynalink.linker.LinkRequest; 38import jdk.nashorn.internal.objects.annotations.Attribute; 39import jdk.nashorn.internal.objects.annotations.Getter; 40import jdk.nashorn.internal.objects.annotations.ScriptClass; 41import jdk.nashorn.internal.runtime.JSType; 42import jdk.nashorn.internal.runtime.PropertyMap; 43import jdk.nashorn.internal.runtime.ScriptObject; 44import jdk.nashorn.internal.runtime.ScriptRuntime; 45import jdk.nashorn.internal.runtime.arrays.ArrayData; 46import jdk.nashorn.internal.runtime.arrays.TypedArrayData; 47 48@ScriptClass("ArrayBufferView") 49abstract class ArrayBufferView extends ScriptObject { 50 private final NativeArrayBuffer buffer; 51 private final int byteOffset; 52 53 // initialized by nasgen 54 private static PropertyMap $nasgenmap$; 55 56 private ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength, final Global global) { 57 super($nasgenmap$); 58 59 final int bytesPerElement = bytesPerElement(); 60 61 checkConstructorArgs(buffer.getByteLength(), bytesPerElement, byteOffset, elementLength); 62 setProto(getPrototype(global)); 63 64 this.buffer = buffer; 65 this.byteOffset = byteOffset; 66 67 assert byteOffset % bytesPerElement == 0; 68 final int start = byteOffset / bytesPerElement; 69 final ByteBuffer newNioBuffer = buffer.getNioBuffer().duplicate().order(ByteOrder.nativeOrder()); 70 final ArrayData data = factory().createArrayData(newNioBuffer, start, start + elementLength); 71 72 setArray(data); 73 } 74 75 protected ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) { 76 this(buffer, byteOffset, elementLength, Global.instance()); 77 } 78 79 private static void checkConstructorArgs(final int byteLength, final int bytesPerElement, final int byteOffset, final int elementLength) { 80 if (byteOffset < 0 || elementLength < 0) { 81 throw new RuntimeException("byteOffset or length must not be negative, byteOffset=" + byteOffset + ", elementLength=" + elementLength + ", bytesPerElement=" + bytesPerElement); 82 } else if (byteOffset + elementLength * bytesPerElement > byteLength) { 83 throw new RuntimeException("byteOffset + byteLength out of range, byteOffset=" + byteOffset + ", elementLength=" + elementLength + ", bytesPerElement=" + bytesPerElement); 84 } else if (byteOffset % bytesPerElement != 0) { 85 throw new RuntimeException("byteOffset must be a multiple of the element size, byteOffset=" + byteOffset + " bytesPerElement=" + bytesPerElement); 86 } 87 } 88 89 private int bytesPerElement() { 90 return factory().bytesPerElement; 91 } 92 93 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 94 public static Object buffer(final Object self) { 95 return ((ArrayBufferView)self).buffer; 96 } 97 98 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 99 public static int byteOffset(final Object self) { 100 return ((ArrayBufferView)self).byteOffset; 101 } 102 103 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 104 public static int byteLength(final Object self) { 105 final ArrayBufferView view = (ArrayBufferView)self; 106 return ((TypedArrayData<?>)view.getArray()).getElementLength() * view.bytesPerElement(); 107 } 108 109 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 110 public static int length(final Object self) { 111 return ((ArrayBufferView)self).elementLength(); 112 } 113 114 @Override 115 public final Object getLength() { 116 return elementLength(); 117 } 118 119 private int elementLength() { 120 return ((TypedArrayData<?>)getArray()).getElementLength(); 121 } 122 123 protected static abstract class Factory { 124 final int bytesPerElement; 125 final int maxElementLength; 126 127 public Factory(final int bytesPerElement) { 128 this.bytesPerElement = bytesPerElement; 129 this.maxElementLength = Integer.MAX_VALUE / bytesPerElement; 130 } 131 132 public final ArrayBufferView construct(final int elementLength) { 133 if (elementLength > maxElementLength) { 134 throw rangeError("inappropriate.array.buffer.length", JSType.toString(elementLength)); 135 } 136 return construct(new NativeArrayBuffer(elementLength * bytesPerElement), 0, elementLength); 137 } 138 139 public abstract ArrayBufferView construct(NativeArrayBuffer buffer, int byteOffset, int elementLength); 140 141 public abstract TypedArrayData<?> createArrayData(ByteBuffer nb, int start, int end); 142 143 public abstract String getClassName(); 144 } 145 146 protected abstract Factory factory(); 147 148 protected abstract ScriptObject getPrototype(final Global global); 149 150 @Override 151 public final String getClassName() { 152 return factory().getClassName(); 153 } 154 155 protected boolean isFloatArray() { 156 return false; 157 } 158 159 protected static ArrayBufferView constructorImpl(final boolean newObj, final Object[] args, final Factory factory) { 160 final Object arg0 = args.length != 0 ? args[0] : 0; 161 final ArrayBufferView dest; 162 final int length; 163 164 if (!newObj) { 165 throw typeError("constructor.requires.new", factory.getClassName()); 166 } 167 168 169 if (arg0 instanceof NativeArrayBuffer) { 170 // Constructor(ArrayBuffer buffer, optional unsigned long byteOffset, optional unsigned long length) 171 final NativeArrayBuffer buffer = (NativeArrayBuffer)arg0; 172 final int byteOffset = args.length > 1 ? JSType.toInt32(args[1]) : 0; 173 174 if (args.length > 2) { 175 length = JSType.toInt32(args[2]); 176 } else { 177 if ((buffer.getByteLength() - byteOffset) % factory.bytesPerElement != 0) { 178 throw new RuntimeException("buffer.byteLength - byteOffset must be a multiple of the element size"); 179 } 180 length = (buffer.getByteLength() - byteOffset) / factory.bytesPerElement; 181 } 182 183 return factory.construct(buffer, byteOffset, length); 184 } else if (arg0 instanceof ArrayBufferView) { 185 // Constructor(TypedArray array) 186 length = ((ArrayBufferView)arg0).elementLength(); 187 dest = factory.construct(length); 188 } else if (arg0 instanceof NativeArray) { 189 // Constructor(type[] array) 190 length = lengthToInt(((NativeArray) arg0).getArray().length()); 191 dest = factory.construct(length); 192 } else { 193 // Constructor(unsigned long length). Treating infinity as 0 is a special case for ArrayBufferView. 194 final double dlen = JSType.toNumber(arg0); 195 length = lengthToInt(Double.isInfinite(dlen) ? 0L : JSType.toLong(dlen)); 196 return factory.construct(length); 197 } 198 199 copyElements(dest, length, (ScriptObject)arg0, 0); 200 201 return dest; 202 } 203 204 protected static Object setImpl(final Object self, final Object array, final Object offset0) { 205 final ArrayBufferView dest = (ArrayBufferView)self; 206 final int length; 207 if (array instanceof ArrayBufferView) { 208 // void set(TypedArray array, optional unsigned long offset) 209 length = ((ArrayBufferView)array).elementLength(); 210 } else if (array instanceof NativeArray) { 211 // void set(type[] array, optional unsigned long offset) 212 length = (int) (((NativeArray) array).getArray().length() & 0x7fff_ffff); 213 } else { 214 throw new RuntimeException("argument is not of array type"); 215 } 216 217 final ScriptObject source = (ScriptObject)array; 218 final int offset = JSType.toInt32(offset0); // default=0 219 220 if (dest.elementLength() < length + offset || offset < 0) { 221 throw new RuntimeException("offset or array length out of bounds"); 222 } 223 224 copyElements(dest, length, source, offset); 225 226 return ScriptRuntime.UNDEFINED; 227 } 228 229 private static void copyElements(final ArrayBufferView dest, final int length, final ScriptObject source, final int offset) { 230 if (!dest.isFloatArray()) { 231 for (int i = 0, j = offset; i < length; i++, j++) { 232 dest.set(j, source.getInt(i, INVALID_PROGRAM_POINT), 0); 233 } 234 } else { 235 for (int i = 0, j = offset; i < length; i++, j++) { 236 dest.set(j, source.getDouble(i, INVALID_PROGRAM_POINT), 0); 237 } 238 } 239 } 240 241 private static int lengthToInt(final long length) { 242 if (length > Integer.MAX_VALUE || length < 0) { 243 throw rangeError("inappropriate.array.buffer.length", JSType.toString(length)); 244 } 245 return (int)(length & Integer.MAX_VALUE); 246 } 247 248 protected static ScriptObject subarrayImpl(final Object self, final Object begin0, final Object end0) { 249 final ArrayBufferView arrayView = (ArrayBufferView)self; 250 final int byteOffset = arrayView.byteOffset; 251 final int bytesPerElement = arrayView.bytesPerElement(); 252 final int elementLength = arrayView.elementLength(); 253 final int begin = NativeArrayBuffer.adjustIndex(JSType.toInt32(begin0), elementLength); 254 final int end = NativeArrayBuffer.adjustIndex(end0 != ScriptRuntime.UNDEFINED ? JSType.toInt32(end0) : elementLength, elementLength); 255 final int length = Math.max(end - begin, 0); 256 257 assert byteOffset % bytesPerElement == 0; 258 259 //second is byteoffset 260 return arrayView.factory().construct(arrayView.buffer, begin * bytesPerElement + byteOffset, length); 261 } 262 263 @Override 264 protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { 265 final GuardedInvocation inv = getArray().findFastGetIndexMethod(getArray().getClass(), desc, request); 266 if (inv != null) { 267 return inv; 268 } 269 return super.findGetIndexMethod(desc, request); 270 } 271 272 @Override 273 protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { 274 final GuardedInvocation inv = getArray().findFastSetIndexMethod(getArray().getClass(), desc, request); 275 if (inv != null) { 276 return inv; 277 } 278 return super.findSetIndexMethod(desc, request); 279 } 280} 281