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