Skip to content

Commit

Permalink
Convert NativeArrayBuffer, NativeDataView, and TypedArrays to lambda (#…
Browse files Browse the repository at this point in the history
…1709)

This fixes a number of tests.

This also updates the external-array harmony file taken from V8. There are still a few spots that error out with the new file, so I will get to those soon.
  • Loading branch information
camnwalter authored Oct 28, 2024
1 parent 796ae02 commit d0663ed
Show file tree
Hide file tree
Showing 17 changed files with 1,271 additions and 1,341 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,50 @@ public enum ReduceOperation {
REDUCE_RIGHT,
}

/** Implements the methods "every", "filter", "forEach", "map", and "some". */
/**
* Implements the methods "every", "filter", "forEach", "map", and "some" without using an
* IdFunctionObject.
*/
public static Object iterativeMethod(
Context cx,
IterativeOperation operation,
Scriptable scope,
Scriptable thisObj,
Object[] args) {
return iterativeMethod(cx, null, operation, scope, thisObj, args, true);
}

/**
* Implements the methods "every", "filter", "forEach", "map", and "some" using an
* IdFunctionObject.
*/
public static Object iterativeMethod(
Context cx,
IdFunctionObject fun,
IterativeOperation operation,
Scriptable scope,
Scriptable thisObj,
Object[] args) {
return iterativeMethod(cx, fun, operation, scope, thisObj, args, false);
}

private static Object iterativeMethod(
Context cx,
IdFunctionObject fun,
IterativeOperation operation,
Scriptable scope,
Scriptable thisObj,
Object[] args,
boolean skipCoercibleCheck) {
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);

if (IterativeOperation.FIND == operation
|| IterativeOperation.FIND_INDEX == operation
|| IterativeOperation.FIND_LAST == operation
|| IterativeOperation.FIND_LAST_INDEX == operation) {
requireObjectCoercible(cx, o, fun);
if (!skipCoercibleCheck) {
if (IterativeOperation.FIND == operation
|| IterativeOperation.FIND_INDEX == operation
|| IterativeOperation.FIND_LAST == operation
|| IterativeOperation.FIND_LAST_INDEX == operation) {
requireObjectCoercible(cx, o, fun);
}
}

long length = getLengthProperty(cx, o);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@

package org.mozilla.javascript.typedarrays;

import org.mozilla.javascript.AbstractEcmaObjectOperations;
import org.mozilla.javascript.Constructable;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.IdFunctionObject;
import org.mozilla.javascript.IdScriptableObject;
import org.mozilla.javascript.LambdaConstructor;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;

/**
* A NativeArrayBuffer is the backing buffer for a typed array. Used inside JavaScript code, it
* implements the ArrayBuffer interface. Used directly from Java, it simply holds a byte array.
*/
public class NativeArrayBuffer extends IdScriptableObject {
public class NativeArrayBuffer extends ScriptableObject {
private static final long serialVersionUID = 3110411773054879549L;

public static final String CLASS_NAME = "ArrayBuffer";
Expand All @@ -32,8 +34,32 @@ public String getClassName() {
}

public static void init(Context cx, Scriptable scope, boolean sealed) {
NativeArrayBuffer na = new NativeArrayBuffer();
na.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
LambdaConstructor constructor =
new LambdaConstructor(
scope,
"ArrayBuffer",
1,
LambdaConstructor.CONSTRUCTOR_NEW,
NativeArrayBuffer::js_constructor);
constructor.setPrototypePropertyAttributes(DONTENUM | READONLY | PERMANENT);

constructor.defineConstructorMethod(
scope, "isView", 1, NativeArrayBuffer::js_isView, DONTENUM, DONTENUM | READONLY);
constructor.definePrototypeMethod(
scope,
"slice",
2,
(Context lcx, Scriptable lscope, Scriptable thisObj, Object[] args) ->
js_slice(lcx, lscope, thisObj, constructor, args),
DONTENUM,
DONTENUM | READONLY);
constructor.definePrototypeProperty(
cx, "byteLength", NativeArrayBuffer::js_byteLength, DONTENUM | READONLY);

ScriptableObject.defineProperty(scope, CLASS_NAME, constructor, DONTENUM);
if (sealed) {
constructor.sealObject();
}
}

/** Create an empty buffer. */
Expand Down Expand Up @@ -79,8 +105,6 @@ public byte[] getBuffer() {
return buffer;
}

// Actual implementations of actual code

/**
* Return a new buffer that represents a slice of this buffer's content, starting at position
* "start" and ending at position "end". Both values will be "clamped" as per the JavaScript
Expand All @@ -106,120 +130,68 @@ public NativeArrayBuffer slice(double s, double e) {
return newBuf;
}

// Function-calling dispatcher

@Override
public Object execIdCall(
IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
if (!f.hasTag(CLASS_NAME)) {
return super.execIdCall(f, cx, scope, thisObj, args);
}
int id = f.methodId();
switch (id) {
case ConstructorId_isView:
return Boolean.valueOf(
(isArg(args, 0) && (args[0] instanceof NativeArrayBufferView)));

case Id_constructor:
double length = isArg(args, 0) ? ScriptRuntime.toNumber(args[0]) : 0;
return new NativeArrayBuffer(length);

case Id_slice:
NativeArrayBuffer self = realThis(thisObj, f);
double start = isArg(args, 0) ? ScriptRuntime.toNumber(args[0]) : 0;
double end = isArg(args, 1) ? ScriptRuntime.toNumber(args[1]) : self.buffer.length;
return self.slice(start, end);
}
throw new IllegalArgumentException(String.valueOf(id));
private static NativeArrayBuffer getSelf(Scriptable thisObj) {
return LambdaConstructor.convertThisObject(thisObj, NativeArrayBuffer.class);
}

private static NativeArrayBuffer realThis(Scriptable thisObj, IdFunctionObject f) {
return ensureType(thisObj, NativeArrayBuffer.class, f);
private static NativeArrayBuffer js_constructor(Context cx, Scriptable scope, Object[] args) {
double length = isArg(args, 0) ? ScriptRuntime.toNumber(args[0]) : 0;
return new NativeArrayBuffer(length);
}

private static boolean isArg(Object[] args, int i) {
return ((args.length > i) && !Undefined.instance.equals(args[i]));
private static Boolean js_isView(
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
return Boolean.valueOf((isArg(args, 0) && (args[0] instanceof NativeArrayBufferView)));
}

@Override
protected void initPrototypeId(int id) {
String s;
int arity;
switch (id) {
case Id_constructor:
arity = 1;
s = "constructor";
break;
case Id_slice:
arity = 2;
s = "slice";
break;
default:
throw new IllegalArgumentException(String.valueOf(id));
private static NativeArrayBuffer js_slice(
Context cx,
Scriptable scope,
Scriptable thisObj,
LambdaConstructor defaultConstructor,
Object[] args) {
NativeArrayBuffer self = getSelf(thisObj);
double start = isArg(args, 0) ? ScriptRuntime.toNumber(args[0]) : 0;
double end = isArg(args, 1) ? ScriptRuntime.toNumber(args[1]) : self.getLength();
int endI =
ScriptRuntime.toInt32(
Math.max(
0,
Math.min(
self.getLength(),
(end < 0 ? self.getLength() + end : end))));
int startI =
ScriptRuntime.toInt32(
Math.min(
endI, Math.max(0, (start < 0 ? self.getLength() + start : start))));
int len = endI - startI;

Constructable constructor =
AbstractEcmaObjectOperations.speciesConstructor(cx, thisObj, defaultConstructor);
Scriptable newBuf = constructor.construct(cx, scope, new Object[] {len});
if (!(newBuf instanceof NativeArrayBuffer)) {
throw ScriptRuntime.typeErrorById("msg.species.invalid.ctor");
}
initPrototypeMethod(CLASS_NAME, id, s, arity);
}
NativeArrayBuffer buf = (NativeArrayBuffer) newBuf;

@Override
protected int findPrototypeId(String s) {
int id;
switch (s) {
case "constructor":
id = Id_constructor;
break;
case "slice":
id = Id_slice;
break;
default:
id = 0;
break;
if (buf == self) {
throw ScriptRuntime.typeErrorById("msg.arraybuf.same");
}
return id;
}

// Table of all functions
private static final int Id_constructor = 1, Id_slice = 2, MAX_PROTOTYPE_ID = Id_slice;

// Constructor (aka static) functions here

private static final int ConstructorId_isView = -1;

@Override
protected void fillConstructorProperties(IdFunctionObject ctor) {
addIdFunctionProperty(ctor, CLASS_NAME, ConstructorId_isView, "isView", 1);
}

// Properties here

@Override
protected int getMaxInstanceId() {
return MAX_INSTANCE_ID;
}

@Override
protected String getInstanceIdName(int id) {
if (id == Id_byteLength) {
return "byteLength";
int actualLength = buf.getLength();
if (actualLength < len) {
throw ScriptRuntime.typeErrorById("msg.arraybuf.smaller.len", len, actualLength);
}
return super.getInstanceIdName(id);
}

@Override
protected Object getInstanceIdValue(int id) {
if (id == Id_byteLength) {
return ScriptRuntime.wrapInt(buffer.length);
}
return super.getInstanceIdValue(id);
System.arraycopy(self.buffer, startI, buf.buffer, 0, len);
return buf;
}

@Override
protected int findInstanceIdInfo(String s) {
if ("byteLength".equals(s)) {
return instanceIdInfo(READONLY | PERMANENT, Id_byteLength);
}
return super.findInstanceIdInfo(s);
private static Object js_byteLength(Scriptable thisObj) {
return getSelf(thisObj).getLength();
}

// Table of all properties
private static final int Id_byteLength = 1, MAX_INSTANCE_ID = Id_byteLength;
private static boolean isArg(Object[] args, int i) {
return ((args.length > i) && !Undefined.instance.equals(args[i]));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
package org.mozilla.javascript.typedarrays;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.IdScriptableObject;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Symbol;
import org.mozilla.javascript.SymbolKey;
import org.mozilla.javascript.Undefined;
Expand All @@ -19,7 +18,7 @@
* NativeArrayBuffer. Many views may simultaneously share the same buffer, and changes to one will
* affect all.
*/
public abstract class NativeArrayBufferView extends IdScriptableObject {
public abstract class NativeArrayBufferView extends ScriptableObject {
private static final long serialVersionUID = 6884475582973958419L;

private static Boolean useLittleEndian = null;
Expand Down Expand Up @@ -76,74 +75,11 @@ protected static boolean isArg(Object[] args, int i) {
return ((args.length > i) && !Undefined.instance.equals(args[i]));
}

// Property dispatcher

@Override
protected int getMaxInstanceId() {
return MAX_INSTANCE_ID;
}

@Override
protected String getInstanceIdName(int id) {
switch (id) {
case Id_buffer:
return "buffer";
case Id_byteOffset:
return "byteOffset";
case Id_byteLength:
return "byteLength";
default:
return super.getInstanceIdName(id);
}
}

@Override
protected Object getInstanceIdValue(int id) {
switch (id) {
case Id_buffer:
return arrayBuffer;
case Id_byteOffset:
return ScriptRuntime.wrapInt(offset);
case Id_byteLength:
return ScriptRuntime.wrapInt(byteLength);
default:
return super.getInstanceIdValue(id);
}
}

@Override
protected int findInstanceIdInfo(String s) {
int id;
switch (s) {
case "buffer":
id = Id_buffer;
break;
case "byteOffset":
id = Id_byteOffset;
break;
case "byteLength":
id = Id_byteLength;
break;
default:
id = 0;
break;
}
if (id == 0) {
return super.findInstanceIdInfo(s);
}
return instanceIdInfo(READONLY | PERMANENT, id);
}

@Override
public Object get(Symbol key, Scriptable start) {
if (SymbolKey.TO_STRING_TAG.equals(key)) {
return getClassName();
}
return super.get(key, start);
}

private static final int Id_buffer = 1, Id_byteOffset = 2, Id_byteLength = 3;

// to be visible by subclasses
protected static final int MAX_INSTANCE_ID = Id_byteLength;
}
Loading

0 comments on commit d0663ed

Please sign in to comment.