Thu, 25 Sep 2014 15:53:47 +0200
8025435: Optimistic builtins support, implemented initial optimistic versions of push, pop, and charCodeAt
Reviewed-by: hannesw, attila, sundar
jlaskey@3 | 1 | /* |
jlaskey@7 | 2 | * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
jlaskey@3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jlaskey@3 | 4 | * |
jlaskey@3 | 5 | * This code is free software; you can redistribute it and/or modify it |
jlaskey@3 | 6 | * under the terms of the GNU General Public License version 2 only, as |
jlaskey@3 | 7 | * published by the Free Software Foundation. Oracle designates this |
jlaskey@3 | 8 | * particular file as subject to the "Classpath" exception as provided |
jlaskey@3 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
jlaskey@3 | 10 | * |
jlaskey@3 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jlaskey@3 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jlaskey@3 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jlaskey@3 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
jlaskey@3 | 15 | * accompanied this code). |
jlaskey@3 | 16 | * |
jlaskey@3 | 17 | * You should have received a copy of the GNU General Public License version |
jlaskey@3 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
jlaskey@3 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jlaskey@3 | 20 | * |
jlaskey@3 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
jlaskey@3 | 22 | * or visit www.oracle.com if you need additional information or have any |
jlaskey@3 | 23 | * questions. |
jlaskey@3 | 24 | */ |
jlaskey@3 | 25 | |
jlaskey@3 | 26 | package jdk.nashorn.internal.objects; |
jlaskey@3 | 27 | |
attila@446 | 28 | import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; |
attila@963 | 29 | import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; |
attila@963 | 30 | import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; |
attila@446 | 31 | |
attila@963 | 32 | import java.nio.ByteBuffer; |
attila@963 | 33 | import java.nio.ByteOrder; |
lagergren@1028 | 34 | |
attila@963 | 35 | import jdk.internal.dynalink.CallSiteDescriptor; |
attila@963 | 36 | import jdk.internal.dynalink.linker.GuardedInvocation; |
attila@963 | 37 | import jdk.internal.dynalink.linker.LinkRequest; |
jlaskey@3 | 38 | import jdk.nashorn.internal.objects.annotations.Attribute; |
jlaskey@3 | 39 | import jdk.nashorn.internal.objects.annotations.Getter; |
jlaskey@3 | 40 | import jdk.nashorn.internal.objects.annotations.ScriptClass; |
jlaskey@3 | 41 | import jdk.nashorn.internal.runtime.JSType; |
hannesw@380 | 42 | import jdk.nashorn.internal.runtime.PropertyMap; |
jlaskey@3 | 43 | import jdk.nashorn.internal.runtime.ScriptObject; |
jlaskey@3 | 44 | import jdk.nashorn.internal.runtime.ScriptRuntime; |
jlaskey@3 | 45 | import jdk.nashorn.internal.runtime.arrays.ArrayData; |
attila@963 | 46 | import jdk.nashorn.internal.runtime.arrays.TypedArrayData; |
jlaskey@3 | 47 | |
jlaskey@3 | 48 | @ScriptClass("ArrayBufferView") |
jlaskey@3 | 49 | abstract class ArrayBufferView extends ScriptObject { |
attila@963 | 50 | private final NativeArrayBuffer buffer; |
attila@963 | 51 | private final int byteOffset; |
jlaskey@3 | 52 | |
hannesw@380 | 53 | // initialized by nasgen |
hannesw@380 | 54 | private static PropertyMap $nasgenmap$; |
hannesw@380 | 55 | |
sundar@418 | 56 | private ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength, final Global global) { |
sundar@771 | 57 | super($nasgenmap$); |
attila@963 | 58 | |
attila@963 | 59 | final int bytesPerElement = bytesPerElement(); |
attila@963 | 60 | |
attila@963 | 61 | checkConstructorArgs(buffer.getByteLength(), bytesPerElement, byteOffset, elementLength); |
attila@963 | 62 | setProto(getPrototype(global)); |
attila@963 | 63 | |
attila@963 | 64 | this.buffer = buffer; |
attila@963 | 65 | this.byteOffset = byteOffset; |
attila@963 | 66 | |
attila@963 | 67 | assert byteOffset % bytesPerElement == 0; |
attila@963 | 68 | final int start = byteOffset / bytesPerElement; |
attila@963 | 69 | final ByteBuffer newNioBuffer = buffer.getNioBuffer().duplicate().order(ByteOrder.nativeOrder()); |
attila@963 | 70 | final ArrayData data = factory().createArrayData(newNioBuffer, start, start + elementLength); |
attila@963 | 71 | |
attila@963 | 72 | setArray(data); |
jlaskey@3 | 73 | } |
jlaskey@3 | 74 | |
attila@963 | 75 | protected ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) { |
sundar@418 | 76 | this(buffer, byteOffset, elementLength, Global.instance()); |
sundar@418 | 77 | } |
sundar@418 | 78 | |
attila@963 | 79 | private static void checkConstructorArgs(final int byteLength, final int bytesPerElement, final int byteOffset, final int elementLength) { |
jlaskey@3 | 80 | if (byteOffset < 0 || elementLength < 0) { |
attila@963 | 81 | throw new RuntimeException("byteOffset or length must not be negative, byteOffset=" + byteOffset + ", elementLength=" + elementLength + ", bytesPerElement=" + bytesPerElement); |
attila@963 | 82 | } else if (byteOffset + elementLength * bytesPerElement > byteLength) { |
attila@963 | 83 | throw new RuntimeException("byteOffset + byteLength out of range, byteOffset=" + byteOffset + ", elementLength=" + elementLength + ", bytesPerElement=" + bytesPerElement); |
attila@963 | 84 | } else if (byteOffset % bytesPerElement != 0) { |
attila@963 | 85 | throw new RuntimeException("byteOffset must be a multiple of the element size, byteOffset=" + byteOffset + " bytesPerElement=" + bytesPerElement); |
jlaskey@3 | 86 | } |
jlaskey@3 | 87 | } |
jlaskey@3 | 88 | |
jlaskey@3 | 89 | private int bytesPerElement() { |
jlaskey@3 | 90 | return factory().bytesPerElement; |
jlaskey@3 | 91 | } |
jlaskey@3 | 92 | |
jlaskey@3 | 93 | @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) |
jlaskey@3 | 94 | public static Object buffer(final Object self) { |
attila@963 | 95 | return ((ArrayBufferView)self).buffer; |
jlaskey@3 | 96 | } |
jlaskey@3 | 97 | |
jlaskey@3 | 98 | @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) |
attila@963 | 99 | public static int byteOffset(final Object self) { |
attila@963 | 100 | return ((ArrayBufferView)self).byteOffset; |
jlaskey@3 | 101 | } |
jlaskey@3 | 102 | |
jlaskey@3 | 103 | @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) |
attila@963 | 104 | public static int byteLength(final Object self) { |
jlaskey@3 | 105 | final ArrayBufferView view = (ArrayBufferView)self; |
attila@963 | 106 | return ((TypedArrayData<?>)view.getArray()).getElementLength() * view.bytesPerElement(); |
jlaskey@3 | 107 | } |
jlaskey@3 | 108 | |
jlaskey@3 | 109 | @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) |
attila@963 | 110 | public static int length(final Object self) { |
jlaskey@3 | 111 | return ((ArrayBufferView)self).elementLength(); |
jlaskey@3 | 112 | } |
jlaskey@3 | 113 | |
jlaskey@3 | 114 | @Override |
jlaskey@3 | 115 | public final Object getLength() { |
jlaskey@3 | 116 | return elementLength(); |
jlaskey@3 | 117 | } |
jlaskey@3 | 118 | |
jlaskey@3 | 119 | private int elementLength() { |
attila@963 | 120 | return ((TypedArrayData<?>)getArray()).getElementLength(); |
jlaskey@3 | 121 | } |
jlaskey@3 | 122 | |
jlaskey@3 | 123 | protected static abstract class Factory { |
jlaskey@3 | 124 | final int bytesPerElement; |
attila@446 | 125 | final int maxElementLength; |
jlaskey@3 | 126 | |
jlaskey@3 | 127 | public Factory(final int bytesPerElement) { |
attila@963 | 128 | this.bytesPerElement = bytesPerElement; |
attila@446 | 129 | this.maxElementLength = Integer.MAX_VALUE / bytesPerElement; |
jlaskey@3 | 130 | } |
jlaskey@3 | 131 | |
jlaskey@3 | 132 | public final ArrayBufferView construct(final int elementLength) { |
attila@963 | 133 | if (elementLength > maxElementLength) { |
attila@446 | 134 | throw rangeError("inappropriate.array.buffer.length", JSType.toString(elementLength)); |
attila@446 | 135 | } |
jlaskey@3 | 136 | return construct(new NativeArrayBuffer(elementLength * bytesPerElement), 0, elementLength); |
jlaskey@3 | 137 | } |
jlaskey@3 | 138 | |
jlaskey@3 | 139 | public abstract ArrayBufferView construct(NativeArrayBuffer buffer, int byteOffset, int elementLength); |
jlaskey@3 | 140 | |
attila@963 | 141 | public abstract TypedArrayData<?> createArrayData(ByteBuffer nb, int start, int end); |
attila@963 | 142 | |
attila@963 | 143 | public abstract String getClassName(); |
jlaskey@3 | 144 | } |
jlaskey@3 | 145 | |
jlaskey@3 | 146 | protected abstract Factory factory(); |
jlaskey@3 | 147 | |
sundar@414 | 148 | protected abstract ScriptObject getPrototype(final Global global); |
jlaskey@3 | 149 | |
attila@963 | 150 | @Override |
attila@963 | 151 | public final String getClassName() { |
attila@963 | 152 | return factory().getClassName(); |
attila@963 | 153 | } |
attila@963 | 154 | |
jlaskey@3 | 155 | protected boolean isFloatArray() { |
jlaskey@3 | 156 | return false; |
jlaskey@3 | 157 | } |
jlaskey@3 | 158 | |
attila@963 | 159 | protected static ArrayBufferView constructorImpl(final boolean newObj, final Object[] args, final Factory factory) { |
attila@963 | 160 | final Object arg0 = args.length != 0 ? args[0] : 0; |
attila@963 | 161 | final ArrayBufferView dest; |
attila@963 | 162 | final int length; |
attila@963 | 163 | |
attila@963 | 164 | if (!newObj) { |
attila@963 | 165 | throw typeError("constructor.requires.new", factory.getClassName()); |
attila@963 | 166 | } |
attila@963 | 167 | |
attila@963 | 168 | |
jlaskey@3 | 169 | if (arg0 instanceof NativeArrayBuffer) { |
jlaskey@3 | 170 | // Constructor(ArrayBuffer buffer, optional unsigned long byteOffset, optional unsigned long length) |
attila@963 | 171 | final NativeArrayBuffer buffer = (NativeArrayBuffer)arg0; |
attila@963 | 172 | final int byteOffset = args.length > 1 ? JSType.toInt32(args[1]) : 0; |
attila@963 | 173 | |
jlaskey@3 | 174 | if (args.length > 2) { |
jlaskey@3 | 175 | length = JSType.toInt32(args[2]); |
jlaskey@3 | 176 | } else { |
jlaskey@3 | 177 | if ((buffer.getByteLength() - byteOffset) % factory.bytesPerElement != 0) { |
jlaskey@3 | 178 | throw new RuntimeException("buffer.byteLength - byteOffset must be a multiple of the element size"); |
jlaskey@3 | 179 | } |
jlaskey@3 | 180 | length = (buffer.getByteLength() - byteOffset) / factory.bytesPerElement; |
jlaskey@3 | 181 | } |
attila@963 | 182 | |
jlaskey@3 | 183 | return factory.construct(buffer, byteOffset, length); |
jlaskey@3 | 184 | } else if (arg0 instanceof ArrayBufferView) { |
jlaskey@3 | 185 | // Constructor(TypedArray array) |
jlaskey@3 | 186 | length = ((ArrayBufferView)arg0).elementLength(); |
attila@963 | 187 | dest = factory.construct(length); |
jlaskey@3 | 188 | } else if (arg0 instanceof NativeArray) { |
jlaskey@3 | 189 | // Constructor(type[] array) |
hannesw@334 | 190 | length = lengthToInt(((NativeArray) arg0).getArray().length()); |
attila@963 | 191 | dest = factory.construct(length); |
jlaskey@3 | 192 | } else { |
attila@963 | 193 | // Constructor(unsigned long length). Treating infinity as 0 is a special case for ArrayBufferView. |
attila@963 | 194 | final double dlen = JSType.toNumber(arg0); |
attila@963 | 195 | length = lengthToInt(Double.isInfinite(dlen) ? 0L : JSType.toLong(dlen)); |
jlaskey@3 | 196 | return factory.construct(length); |
jlaskey@3 | 197 | } |
jlaskey@3 | 198 | |
attila@963 | 199 | copyElements(dest, length, (ScriptObject)arg0, 0); |
attila@963 | 200 | |
attila@963 | 201 | return dest; |
jlaskey@3 | 202 | } |
jlaskey@3 | 203 | |
jlaskey@3 | 204 | protected static Object setImpl(final Object self, final Object array, final Object offset0) { |
attila@963 | 205 | final ArrayBufferView dest = (ArrayBufferView)self; |
jlaskey@3 | 206 | final int length; |
jlaskey@3 | 207 | if (array instanceof ArrayBufferView) { |
jlaskey@3 | 208 | // void set(TypedArray array, optional unsigned long offset) |
jlaskey@3 | 209 | length = ((ArrayBufferView)array).elementLength(); |
jlaskey@3 | 210 | } else if (array instanceof NativeArray) { |
jlaskey@3 | 211 | // void set(type[] array, optional unsigned long offset) |
jlaskey@3 | 212 | length = (int) (((NativeArray) array).getArray().length() & 0x7fff_ffff); |
jlaskey@3 | 213 | } else { |
jlaskey@3 | 214 | throw new RuntimeException("argument is not of array type"); |
jlaskey@3 | 215 | } |
jlaskey@3 | 216 | |
attila@963 | 217 | final ScriptObject source = (ScriptObject)array; |
jlaskey@3 | 218 | final int offset = JSType.toInt32(offset0); // default=0 |
jlaskey@3 | 219 | |
jlaskey@3 | 220 | if (dest.elementLength() < length + offset || offset < 0) { |
jlaskey@3 | 221 | throw new RuntimeException("offset or array length out of bounds"); |
jlaskey@3 | 222 | } |
jlaskey@3 | 223 | |
jlaskey@3 | 224 | copyElements(dest, length, source, offset); |
jlaskey@3 | 225 | |
jlaskey@3 | 226 | return ScriptRuntime.UNDEFINED; |
jlaskey@3 | 227 | } |
jlaskey@3 | 228 | |
jlaskey@3 | 229 | private static void copyElements(final ArrayBufferView dest, final int length, final ScriptObject source, final int offset) { |
jlaskey@3 | 230 | if (!dest.isFloatArray()) { |
jlaskey@3 | 231 | for (int i = 0, j = offset; i < length; i++, j++) { |
hannesw@1020 | 232 | dest.set(j, source.getInt(i, INVALID_PROGRAM_POINT), 0); |
jlaskey@3 | 233 | } |
jlaskey@3 | 234 | } else { |
jlaskey@3 | 235 | for (int i = 0, j = offset; i < length; i++, j++) { |
hannesw@1020 | 236 | dest.set(j, source.getDouble(i, INVALID_PROGRAM_POINT), 0); |
jlaskey@3 | 237 | } |
jlaskey@3 | 238 | } |
jlaskey@3 | 239 | } |
jlaskey@3 | 240 | |
hannesw@334 | 241 | private static int lengthToInt(final long length) { |
hannesw@334 | 242 | if (length > Integer.MAX_VALUE || length < 0) { |
hannesw@334 | 243 | throw rangeError("inappropriate.array.buffer.length", JSType.toString(length)); |
hannesw@334 | 244 | } |
attila@963 | 245 | return (int)(length & Integer.MAX_VALUE); |
hannesw@334 | 246 | } |
hannesw@334 | 247 | |
sundar@774 | 248 | protected static ScriptObject subarrayImpl(final Object self, final Object begin0, final Object end0) { |
attila@963 | 249 | final ArrayBufferView arrayView = (ArrayBufferView)self; |
attila@963 | 250 | final int byteOffset = arrayView.byteOffset; |
attila@963 | 251 | final int bytesPerElement = arrayView.bytesPerElement(); |
attila@963 | 252 | final int elementLength = arrayView.elementLength(); |
attila@963 | 253 | final int begin = NativeArrayBuffer.adjustIndex(JSType.toInt32(begin0), elementLength); |
attila@963 | 254 | final int end = NativeArrayBuffer.adjustIndex(end0 != ScriptRuntime.UNDEFINED ? JSType.toInt32(end0) : elementLength, elementLength); |
attila@963 | 255 | final int length = Math.max(end - begin, 0); |
attila@963 | 256 | |
attila@963 | 257 | assert byteOffset % bytesPerElement == 0; |
attila@963 | 258 | |
attila@963 | 259 | //second is byteoffset |
attila@963 | 260 | return arrayView.factory().construct(arrayView.buffer, begin * bytesPerElement + byteOffset, length); |
attila@963 | 261 | } |
attila@963 | 262 | |
attila@963 | 263 | @Override |
attila@963 | 264 | protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { |
attila@963 | 265 | final GuardedInvocation inv = getArray().findFastGetIndexMethod(getArray().getClass(), desc, request); |
attila@963 | 266 | if (inv != null) { |
attila@963 | 267 | return inv; |
attila@963 | 268 | } |
attila@963 | 269 | return super.findGetIndexMethod(desc, request); |
attila@963 | 270 | } |
attila@963 | 271 | |
attila@963 | 272 | @Override |
attila@963 | 273 | protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { |
attila@963 | 274 | final GuardedInvocation inv = getArray().findFastSetIndexMethod(getArray().getClass(), desc, request); |
attila@963 | 275 | if (inv != null) { |
attila@963 | 276 | return inv; |
attila@963 | 277 | } |
attila@963 | 278 | return super.findSetIndexMethod(desc, request); |
jlaskey@3 | 279 | } |
jlaskey@3 | 280 | } |