Skip to content
This repository has been archived by the owner on Jul 14, 2022. It is now read-only.

Commit

Permalink
Add support for external arrays on each ScriptableObject via a new me…
Browse files Browse the repository at this point in the history
…thod:

  ScriptableObject.setExternalArray()
and the new "ExternalArray" class and its subclasses.
This feature lets you set the contents of the "array" portion of an
object to a Java array of various types. This makes it easier to
write code that efficiently shares code between Java and Node.js
See here for more:
  #17
  • Loading branch information
Gregory Brail committed Jul 16, 2014
1 parent 6952f5a commit ff25391
Show file tree
Hide file tree
Showing 12 changed files with 810 additions and 14 deletions.
72 changes: 58 additions & 14 deletions src/org/mozilla/javascript/ScriptableObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.HashSet;
import java.util.Map;

import org.mozilla.javascript.arrays.ExternalArray;
import org.mozilla.javascript.debug.DebuggableObject;
import org.mozilla.javascript.annotations.JSConstructor;
import org.mozilla.javascript.annotations.JSFunction;
Expand Down Expand Up @@ -118,6 +119,8 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
private transient Slot firstAdded;
private transient Slot lastAdded;

// Support for external byte arrays
private ExternalArray externalArray;

private volatile Map<Object,Object> associatedValues;

Expand Down Expand Up @@ -434,6 +437,9 @@ public boolean has(String name, Scriptable start)
*/
public boolean has(int index, Scriptable start)
{
if ((externalArray != null) && externalArray.inRange(index)) {
return true;
}
return null != getSlot(null, index, SLOT_QUERY);
}

Expand Down Expand Up @@ -465,6 +471,10 @@ public Object get(String name, Scriptable start)
*/
public Object get(int index, Scriptable start)
{
if ((externalArray != null) && externalArray.inRange(index)) {
return externalArray.get(index);
}

Slot slot = getSlot(null, index, SLOT_QUERY);
if (slot == null) {
return Scriptable.NOT_FOUND;
Expand Down Expand Up @@ -505,11 +515,15 @@ public void put(String name, Scriptable start, Object value)
*/
public void put(int index, Scriptable start, Object value)
{
if (putImpl(null, index, start, value))
return;
if ((externalArray != null) && externalArray.inRange(index)) {
checkNotSealed(null, index);
externalArray.put(index, value);
} else {
if (putImpl(null, index, start, value))
return;

if (start == this) throw Kit.codeBug();
start.put(index, start, value);
if (start == this) throw Kit.codeBug();
}
}

/**
Expand All @@ -530,14 +544,17 @@ public void delete(String name)
* Removes the indexed property from the object.
*
* If the property is not found, or it has the PERMANENT attribute,
* no action is taken.
* no action is taken. If an external array is used and the index is inside the
* array, then no action is taken either.
*
* @param index the numeric index for the property
*/
public void delete(int index)
{
checkNotSealed(null, index);
removeSlot(null, index);
if ((externalArray == null) || !externalArray.inRange(index)) {
removeSlot(null, index);
}
}

/**
Expand Down Expand Up @@ -2688,6 +2705,25 @@ private boolean putConstImpl(String name, int index, Scriptable start,
return slot.setValue(value, this, start);
}

/**
* Replace the implementation of the "array" slots of this type with the specified array. "put" and "get"
* operations using an "index" in that case are delegated directly to the array. Attempts to set
* items in the array with different values in this case will fail with a TypeError.
*
* @since 1.7R5
*/
public void setExternalArray(ExternalArray array) {
this.externalArray = array;
}

public ExternalArray getExternalArray() {
return externalArray;
}

public boolean hasExternalArray() {
return (externalArray != null);
}

private Slot findAttributeSlot(String name, int index, int accessType)
{
Slot slot = getSlot(name, index, accessType);
Expand Down Expand Up @@ -2957,10 +2993,16 @@ private static void addKnownAbsentSlot(Slot[] slots, Slot slot,

Object[] getIds(boolean getAll) {
Slot[] s = slots;
Object[] a = ScriptRuntime.emptyArgs;
if (s == null)
return a;
int c = 0;
Object[] a;

int al = (s == null ? 0 : s.length) + (externalArray == null ? 0 : externalArray.getLength());
if (al == 0) {
return ScriptRuntime.emptyArgs;
}

a = new Object[al];
int c = (externalArray == null ? 0 : externalArray.copyIds(a));

Slot slot = firstAdded;
while (slot != null && slot.wasDeleted) {
// we used to removed deleted slots from the linked list here
Expand All @@ -2971,8 +3013,6 @@ Object[] getIds(boolean getAll) {
}
while (slot != null) {
if (getAll || (slot.getAttributes() & DONTENUM) == 0) {
if (c == 0)
a = new Object[s.length];
a[c++] = slot.name != null
? slot.name
: Integer.valueOf(slot.indexOrHash);
Expand Down Expand Up @@ -3081,11 +3121,15 @@ protected Slot getSlot(Context cx, Object id, int accessType) {
// a subclass that implements java.util.Map.

public int size() {
return count < 0 ? ~count : count;
int s = (count < 0 ? ~count : count);
if (externalArray != null) {
s += externalArray.getLength();
}
return s;
}

public boolean isEmpty() {
return count == 0 || count == -1;
return ((count == 0) || (count == -1)) && ((externalArray == null) || (externalArray.getLength() == 0));
}


Expand Down
68 changes: 68 additions & 0 deletions src/org/mozilla/javascript/arrays/ExternalArray.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.mozilla.javascript.arrays;

import org.mozilla.javascript.ScriptRuntime;

import java.io.Serializable;

/**
* The abstract base class for the different types of external arrays. Users must construct one of
* the subclasses before passing it to "ScriptableObject.setExternalArray()".
*/

public abstract class ExternalArray
implements Serializable
{
/**
* Return the length of the array.
*/
public abstract int getLength();

/**
* Return true if the given index is in range. Normally users should not need to use this.
*/
public boolean inRange(int index) {
return ((index < getLength()) && (index >= 0));
}

/**
* Return the element at the specified index as an Object.
*/
public Object get(int index) {
return getElement(index);
}

/**
* Set the element at the specified index.
*/
public void put(int index, Object value) {
try {
putElement(index, value);
} catch (ClassCastException cce) {
throw ScriptRuntime.typeError("Invalid object type for external array");
}
}

/**
* Copy numeric ids representing the property IDs of each array element to the specified array.
* Used internally when iterating over the properties of the object.
*/
public int copyIds(Object[] ids) {
int i;
for (i = 0; i < getLength(); i++) {
ids[i] = Integer.valueOf(i);
}
return i;
}

/**
* Return the value of the element in a form that works for a script. Caller will check bounds --
* don't do that.
*/
protected abstract Object getElement(int index);

/**
* Set the value of the element. Don't check bounds. Blind casting is OK -- caller will handle
* ClassCastException -- don't add an instanceof check.
*/
protected abstract void putElement(int index, Object value);
}
41 changes: 41 additions & 0 deletions src/org/mozilla/javascript/arrays/ExternalByteArray.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.javascript.arrays;

/**
* An implementation of the external array using an array of bytes. From a JavaScript perspective,
* only "number" types may be set in the array. Valid values are between -128 and 127, inclusive.
*/

public class ExternalByteArray
extends ExternalArray
{
private static final long serialVersionUID = 5377484970217959212L;

private final byte[] array;

public ExternalByteArray(byte[] array) {
this.array = array;
}

public byte[] getArray() {
return array;
}

protected Object getElement(int index) {
return Integer.valueOf(array[index]);
}

protected void putElement(int index, Object value) {
Number num = (Number)value;
array[index] = num.byteValue();
}

public int getLength() {
return array.length;
}
}
48 changes: 48 additions & 0 deletions src/org/mozilla/javascript/arrays/ExternalClampedByteArray.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.javascript.arrays;

/**
* An implementation of the external array using an array of bytes. From a JavaScript perspective,
* only "number" types may be set in the array. Valid values are between 0 and 255, inclusive. Any values out
* of that range will be "clamped", meaning that values smaller than 0 will be set to 0 and larger than 255 will
* be set to 255.
*/

public class ExternalClampedByteArray
extends ExternalArray
{
private static final long serialVersionUID = -1883561335812409618L;

private final byte[] array;

public ExternalClampedByteArray(byte[] array) {
this.array = array;
}

public byte[] getArray() {
return array;
}

protected Object getElement(int index) {
return array[index] & 0xff;
}

protected void putElement(int index, Object value) {
int val = ((Number)value).intValue();
if (val < 0) {
val = 0;
} else if (val > 255) {
val = 255;
}
array[index] = (byte)(val & 0xff);
}

public int getLength() {
return array.length;
}
}
41 changes: 41 additions & 0 deletions src/org/mozilla/javascript/arrays/ExternalDoubleArray.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.javascript.arrays;

/**
* An implementation of the external array using an array of "double"s. Only "number" types may be set in
* the array.
*/

public class ExternalDoubleArray
extends ExternalArray
{
private static final long serialVersionUID = -773914084068347275L;

private final double[] array;

public ExternalDoubleArray(double[] array) {
this.array = array;
}

public double[] getArray() {
return array;
}

protected Object getElement(int index) {
return array[index];
}

protected void putElement(int index, Object value) {
double val = ((Number)value).doubleValue();
array[index] = val;
}

public int getLength() {
return array.length;
}
}
41 changes: 41 additions & 0 deletions src/org/mozilla/javascript/arrays/ExternalFloatArray.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.javascript.arrays;

/**
* An implementation of the external array using an array of "float"s. Only "number" types may be set in
* the array.
*/

public class ExternalFloatArray
extends ExternalArray
{
private static final long serialVersionUID = 3786769656861013570L;

private final float[] array;

public ExternalFloatArray(float[] array) {
this.array = array;
}

public float[] getArray() {
return array;
}

protected Object getElement(int index) {
return array[index];
}

protected void putElement(int index, Object value) {
float val = ((Number)value).floatValue();
array[index] = val;
}

public int getLength() {
return array.length;
}
}
Loading

0 comments on commit ff25391

Please sign in to comment.