Skip to content

Commit

Permalink
Add generic type support and improve java.util.{List,Map} handling
Browse files Browse the repository at this point in the history
  • Loading branch information
rPraml committed Jan 18, 2021
1 parent 056daee commit 6b4b1e2
Show file tree
Hide file tree
Showing 16 changed files with 919 additions and 59 deletions.
4 changes: 3 additions & 1 deletion examples/PrimitiveWrapFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* 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/. */

import java.lang.reflect.Type;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.WrapFactory;
Expand All @@ -27,7 +29,7 @@
public class PrimitiveWrapFactory extends WrapFactory {
@Override
public Object wrap(Context cx, Scriptable scope, Object obj,
Class<?> staticType)
Type staticType)
{
if (obj instanceof String || obj instanceof Number ||
obj instanceof Boolean)
Expand Down
2 changes: 1 addition & 1 deletion src/org/mozilla/javascript/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -1829,7 +1829,7 @@ public static Scriptable toObject(Object value, Scriptable scope,
* <p>
* The rest of values will be wrapped as LiveConnect objects
* by calling {@link WrapFactory#wrap(Context cx, Scriptable scope,
* Object obj, Class staticType)} as in:
* Object obj, Type staticType)} as in:
* <pre>
* Context cx = Context.getCurrentContext();
* return cx.getWrapFactory().wrap(cx, scope, value, null);
Expand Down
1 change: 1 addition & 0 deletions src/org/mozilla/javascript/IdScriptableObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,7 @@ protected void addIdFunctionProperty(Scriptable obj, Object tag, int id,
* @return obj casted to the target type
* @throws EcmaError if the cast failed.
*/
@SuppressWarnings("unchecked")
protected static <T> T ensureType(Object obj, Class<T> clazz, IdFunctionObject f)
{
if (clazz.isInstance(obj)) {
Expand Down
35 changes: 30 additions & 5 deletions src/org/mozilla/javascript/JavaMembers.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
Expand Down Expand Up @@ -85,18 +86,18 @@ Object get(Scriptable scope, String name, Object javaObject,
}
Context cx = Context.getContext();
Object rval;
Class<?> type;
Type type;
try {
if (member instanceof BeanProperty) {
BeanProperty bp = (BeanProperty) member;
if (bp.getter == null)
return Scriptable.NOT_FOUND;
rval = bp.getter.invoke(javaObject, Context.emptyArgs);
type = bp.getter.method().getReturnType();
type = bp.getter.method().getGenericReturnType();
} else {
Field field = (Field) member;
rval = field.get(isStatic ? null : javaObject);
type = field.getType();
type = field.getGenericType();
}
} catch (Exception ex) {
throw Context.throwAsScriptRuntimeEx(ex);
Expand Down Expand Up @@ -633,6 +634,30 @@ private void reflect(Scriptable scope,
ht.putAll(toAdd);
}

// if we are a Map or Iterable, we add an iterator in order
// that the JavaObject can be used in 'for(key in o)' or
// 'for each (value in o)' loops
if (Map.class.isAssignableFrom(cl)) {
// Add Map iterator
members.put(NativeIterator.ITERATOR_PROPERTY_NAME,
NativeIterator.JAVA_MAP_ITERATOR);

} else if (Iterable.class.isAssignableFrom(cl)) {
// Add Iterable/Collection iterator
members.put(NativeIterator.ITERATOR_PROPERTY_NAME,
NativeIterator.JAVA_COLLECTION_ITERATOR);
// look for size() method and register as length property
Object member = members.get("size");
if (member instanceof NativeJavaMethod) {
NativeJavaMethod njmGet = (NativeJavaMethod) member;
MemberBox sizeMethod = extractGetMethod(njmGet.methods, false);
if (sizeMethod != null) {
BeanProperty bp = new BeanProperty(sizeMethod, null, null);
members.put("length", bp);
}
}
}

// Reflect constructors
Constructor<?>[] constructors = getAccessibleConstructors(includePrivate);
MemberBox[] ctorMembers = new MemberBox[constructors.length];
Expand Down Expand Up @@ -896,10 +921,10 @@ public Object getDefaultValue(Class<?> hint)
if (hint == ScriptRuntime.FunctionClass)
return this;
Object rval;
Class<?> type;
Type type;
try {
rval = field.get(javaObject);
type = field.getType();
type = field.getGenericType();
} catch (IllegalAccessException accEx) {
throw Context.reportRuntimeErrorById(
"msg.java.internal.private", field.getName());
Expand Down
71 changes: 71 additions & 0 deletions src/org/mozilla/javascript/NativeIterator.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

package org.mozilla.javascript;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

/**
* This class implements iterator objects. See
Expand All @@ -18,6 +20,10 @@ public final class NativeIterator extends IdScriptableObject {
private static final long serialVersionUID = -4136968203581667681L;
private static final Object ITERATOR_TAG = "Iterator";

// Functions are registered as '__iterator__' for Iterables and Maps
public static final BaseFunction JAVA_COLLECTION_ITERATOR = new CollectionIteratorFunction();
public static final BaseFunction JAVA_MAP_ITERATOR = new MapIteratorFunction();

static void init(Context cx, ScriptableObject scope, boolean sealed) {
// Iterator
NativeIterator iterator = new NativeIterator();
Expand Down Expand Up @@ -221,6 +227,71 @@ static private Iterator<?> getJavaIterator(Object obj) {
return null;
}

static class CollectionIteratorFunction extends BaseFunction {
@Override
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) {

Object wrapped = ((NativeJavaObject) thisObj).javaObject;
if (Boolean.TRUE.equals(args[0])) {
// key only iterator, we will return an iterator
// for the sequence of the collection length.
int length = ((Collection<?>) wrapped).size();
return cx.getWrapFactory().wrap(cx, scope,
new SequenceIterator(length, scope),
WrappedJavaIterator.class);
} else {
Iterator<?> iter = ((Iterable<?>) wrapped).iterator();
return cx.getWrapFactory().wrap(cx, scope,
new WrappedJavaIterator(iter, scope),
WrappedJavaIterator.class);
}
}
}

static public class SequenceIterator
{
SequenceIterator(int size, Scriptable scope) {
this.size = size;
this.scope = scope;
}

public Object next() {
if (pos >= size) {
// Out of values. Throw StopIteration.
throw new JavaScriptException(
NativeIterator.getStopIterationObject(scope), null, 0);
}
return pos++;
}

public Object __iterator__(boolean b) {
return this;
}

private int size;
private int pos;
private Scriptable scope;
}

static class MapIteratorFunction extends BaseFunction {
@Override
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) {

Map<?, ?> map = (Map<?, ?>) ((NativeJavaObject) thisObj).javaObject;
Iterator<?> iter;
if (Boolean.TRUE.equals(args[0])) {
iter = map.keySet().iterator();
} else {
iter = map.values().iterator();
}
return cx.getWrapFactory().wrap(cx, scope,
new WrappedJavaIterator(iter, scope),
WrappedJavaIterator.class);
}
}

static public class WrappedJavaIterator
{
WrappedJavaIterator(Iterator<?> iterator, Scriptable scope) {
Expand Down
53 changes: 33 additions & 20 deletions src/org/mozilla/javascript/NativeJavaList.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,34 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class NativeJavaList extends NativeJavaObject {

private static final long serialVersionUID = 6403865639690547921L;

private List<Object> list;

private Class<?> valueType;

@SuppressWarnings("unchecked")
public NativeJavaList(Scriptable scope, Object list) {
super(scope, list, list.getClass());
public NativeJavaList(Scriptable scope, Object list, Type staticType) {
super(scope, list, staticType);
assert list instanceof List;
this.list = (List<Object>) list;
if (staticType == null) {
staticType = list.getClass().getGenericSuperclass();
}
if (staticType instanceof ParameterizedType) {
Type[] types = ((ParameterizedType) staticType).getActualTypeArguments();
// types[0] contains the T of 'List<T>'
this.valueType = ScriptRuntime.getRawType(types[0]);
} else {
this.valueType = Object.class;
}
}

@Override
Expand All @@ -24,14 +41,6 @@ public String getClassName() {
}


@Override
public boolean has(String name, Scriptable start) {
if (name.equals("length")) {
return true;
}
return super.has(name, start);
}

@Override
public boolean has(int index, Scriptable start) {
if (isWithValidIndex(index)) {
Expand All @@ -48,14 +57,6 @@ public boolean has(Symbol key, Scriptable start) {
return super.has(key, start);
}

@Override
public Object get(String name, Scriptable start) {
if ("length".equals(name)) {
return Integer.valueOf(list.size());
}
return super.get(name, start);
}

@Override
public Object get(int index, Scriptable start) {
if (isWithValidIndex(index)) {
Expand All @@ -76,13 +77,25 @@ public Object get(Symbol key, Scriptable start) {

@Override
public void put(int index, Scriptable start, Object value) {
if (isWithValidIndex(index)) {
list.set(index, Context.jsToJava(value, Object.class));
if (index >= 0) {
ensureCapacity(index + 1);
list.set(index, Context.jsToJava(value, valueType));
return;
}
super.put(index, start, value);
}

private void ensureCapacity(int minCapacity) {
if (minCapacity > list.size()) {
if (list instanceof ArrayList) {
((ArrayList<?>) list).ensureCapacity(minCapacity);
}
while (minCapacity > list.size()) {
list.add(null);
}
}
}

@Override
public Object[] getIds() {
List<?> list = (List<?>) javaObject;
Expand Down
Loading

0 comments on commit 6b4b1e2

Please sign in to comment.