• Home
  • History
  • Annotate
  • only in this directory
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