Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 9 additions & 22 deletions src/org/mozilla/javascript/NativeMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,30 +187,17 @@ static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject map,
// Find the "add" function of our own prototype, since it might have
// been replaced. Since we're not fully constructed yet, create a dummy instance
// so that we can get our own prototype.
ScriptableObject dummy = ensureScriptableObject(cx.newObject(scope, map.getClassName()));
final Callable set =
ScriptRuntime.getPropFunctionAndThis(dummy.getPrototype(), "set", cx, scope);
Scriptable proto = ScriptableObject.getClassPrototype(scope, map.getClassName());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the only code change in this class.
I have changed this to getClassPrototype instead of creating a dummy object and I reuse the code in
ScriptRuntime.loadFromIterable

final Callable set = ScriptRuntime.getPropFunctionAndThis(proto, "set", cx, scope);
ScriptRuntime.lastStoredScriptable(cx);

// Finally, run through all the iterated values and add them!
try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, ito)) {
for (Object val : it) {
Scriptable sVal = ScriptableObject.ensureScriptable(val);
if (sVal instanceof Symbol) {
throw ScriptRuntime.typeErrorById(
"msg.arg.not.object", ScriptRuntime.typeof(sVal));
}
Object finalKey = sVal.get(0, sVal);
if (finalKey == NOT_FOUND) {
finalKey = Undefined.instance;
}
Object finalVal = sVal.get(1, sVal);
if (finalVal == NOT_FOUND) {
finalVal = Undefined.instance;
}
set.call(cx, scope, map, new Object[] {finalKey, finalVal});
}
}
ScriptRuntime.loadFromIterable(
cx,
scope,
arg1,
(key, value) -> {
set.call(cx, scope, map, new Object[] {key, value});
});
}

private static NativeMap realThis(Scriptable thisObj, IdFunctionObject f) {
Expand Down
132 changes: 117 additions & 15 deletions src/org/mozilla/javascript/NativeObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import org.mozilla.javascript.ScriptRuntime.StringIdOrIndex;
Expand Down Expand Up @@ -47,6 +49,9 @@ protected void fillConstructorProperties(IdFunctionObject ctor) {
if (Context.getCurrentContext().version >= Context.VERSION_ES6) {
addIdFunctionProperty(
ctor, OBJECT_TAG, ConstructorId_setPrototypeOf, "setPrototypeOf", 2);
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_entries, "entries", 1);
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_fromEntries, "fromEntries", 1);
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_values, "values", 1);
}
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_keys, "keys", 1);
addIdFunctionProperty(
Expand Down Expand Up @@ -224,11 +229,7 @@ public Object execIdCall(

if (arg instanceof Symbol) {
result = ((SymbolScriptable) thisObj).has((Symbol) arg, thisObj);
if (result && thisObj instanceof ScriptableObject) {
ScriptableObject so = (ScriptableObject) thisObj;
int attrs = so.getAttributes((Symbol) arg);
result = ((attrs & ScriptableObject.DONTENUM) == 0);
}
result = result && isEnumerable((Symbol) arg, thisObj);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved code to "isEnumerable" method

} else {
StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, arg);
// When checking if a property is enumerable, a missing property should
Expand All @@ -237,18 +238,10 @@ public Object execIdCall(
try {
if (s.stringId == null) {
result = thisObj.has(s.index, thisObj);
if (result && thisObj instanceof ScriptableObject) {
ScriptableObject so = (ScriptableObject) thisObj;
int attrs = so.getAttributes(s.index);
result = ((attrs & ScriptableObject.DONTENUM) == 0);
}
result = result && isEnumerable(s.index, thisObj);
} else {
result = thisObj.has(s.stringId, thisObj);
if (result && thisObj instanceof ScriptableObject) {
ScriptableObject so = (ScriptableObject) thisObj;
int attrs = so.getAttributes(s.stringId);
result = ((attrs & ScriptableObject.DONTENUM) == 0);
}
result = result && isEnumerable(s.stringId, thisObj);
}
} catch (EvaluatorException ee) {
if (ee.getMessage()
Expand Down Expand Up @@ -403,6 +396,80 @@ public Object execIdCall(
}
return cx.newArray(scope, ids);
}

case ConstructorId_entries:
{
Object arg = args.length < 1 ? Undefined.instance : args[0];
Scriptable obj = getCompatibleObject(cx, scope, arg);
Object[] ids = obj.getIds();
int j = 0;
for (int i = 0; i < ids.length; i++) {
if (ids[i] instanceof Integer) {
int intId = (Integer) ids[i];
if (obj.has(intId, obj) && isEnumerable(intId, obj)) {
String stringId = ScriptRuntime.toString(ids[i]);
Object[] entry = new Object[] {stringId, obj.get(intId, obj)};
ids[j++] = cx.newArray(scope, entry);
}
} else {
String stringId = ScriptRuntime.toString(ids[i]);
if (obj.has(stringId, obj) && isEnumerable(stringId, obj)) {
Object[] entry = new Object[] {stringId, obj.get(stringId, obj)};
ids[j++] = cx.newArray(scope, entry);
}
}
}
if (j != ids.length) {
ids = Arrays.copyOf(ids, j);
}
return cx.newArray(scope, ids);
}
case ConstructorId_fromEntries:
{
Object arg = args.length < 1 ? Undefined.instance : args[0];
arg = getCompatibleObject(cx, scope, arg);
Scriptable obj = cx.newObject(scope);
ScriptRuntime.loadFromIterable(
cx,
scope,
arg,
(key, value) -> {
if (key instanceof Integer) {
obj.put((Integer) key, obj, value);
} else if (key instanceof Symbol
&& obj instanceof SymbolScriptable) {
((SymbolScriptable) obj).put((Symbol) key, obj, value);
} else {
obj.put(ScriptRuntime.toString(key), obj, value);
}
});
return obj;
}
case ConstructorId_values:
{
Object arg = args.length < 1 ? Undefined.instance : args[0];
Scriptable obj = getCompatibleObject(cx, scope, arg);
Object[] ids = obj.getIds();
int j = 0;
for (int i = 0; i < ids.length; i++) {
if (ids[i] instanceof Integer) {
int intId = (Integer) ids[i];
if (obj.has(intId, obj) && isEnumerable(intId, obj)) {
ids[j++] = obj.get(intId, obj);
}
} else {
String stringId = ScriptRuntime.toString(ids[i]);
// getter may remove keys
if (obj.has(stringId, obj) && isEnumerable(stringId, obj)) {
ids[j++] = obj.get(stringId, obj);
}
}
}
if (j != ids.length) {
ids = Arrays.copyOf(ids, j);
}
return cx.newArray(scope, ids);
}
case ConstructorId_getOwnPropertyNames:
{
Object arg = args.length < 1 ? Undefined.instance : args[0];
Expand Down Expand Up @@ -640,6 +707,36 @@ && isFalse(desc.get("writable"))) {
}
}

private boolean isEnumerable(int index, Object obj) {
if (obj instanceof ScriptableObject) {
ScriptableObject so = (ScriptableObject) obj;
int attrs = so.getAttributes(index);
return (attrs & ScriptableObject.DONTENUM) == 0;
} else {
return true;
}
}

private boolean isEnumerable(String key, Object obj) {
if (obj instanceof ScriptableObject) {
ScriptableObject so = (ScriptableObject) obj;
int attrs = so.getAttributes(key);
return (attrs & ScriptableObject.DONTENUM) == 0;
} else {
return true;
}
}

private boolean isEnumerable(Symbol sym, Object obj) {
if (obj instanceof ScriptableObject) {
ScriptableObject so = (ScriptableObject) obj;
int attrs = so.getAttributes(sym);
return (attrs & ScriptableObject.DONTENUM) == 0;
} else {
return true;
}
}

private static Scriptable getCompatibleObject(Context cx, Scriptable scope, Object arg) {
if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
Scriptable s = ScriptRuntime.toObject(cx, scope, arg);
Expand Down Expand Up @@ -931,7 +1028,12 @@ protected int findPrototypeId(String s) {
ConstructorId_getOwnPropertySymbols = -14,
ConstructorId_assign = -15,
ConstructorId_is = -16,

// ES6
ConstructorId_setPrototypeOf = -17,
ConstructorId_entries = -18,
ConstructorId_fromEntries = -19,
ConstructorId_values = -20,
Id_constructor = 1,
Id_toString = 2,
Id_toLocaleString = 3,
Expand Down
67 changes: 60 additions & 7 deletions src/org/mozilla/javascript/ScriptRuntime.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Arrays;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.function.BiConsumer;
import org.mozilla.javascript.ast.FunctionNode;
import org.mozilla.javascript.v8dtoa.DoubleConversion;
import org.mozilla.javascript.v8dtoa.FastDtoa;
Expand Down Expand Up @@ -2417,6 +2418,50 @@ private static void enumChangeObject(IdEnumeration x) {
x.index = 0;
}

/**
* This is used to handle all the special cases that are required when invoking
* Object.fromEntries or constructing a NativeMap or NativeWeakMap from an iterable.
*
* @param cx the current context
* @param scope the current scope
* @param arg1 the iterable object.
* @param setter the setter to set the value
* @return true, if arg1 was iterable.
*/
public static boolean loadFromIterable(
Context cx, Scriptable scope, Object arg1, BiConsumer<Object, Object> setter) {
if ((arg1 == null) || Undefined.instance.equals(arg1)) {
return false;
}

// Call the "[Symbol.iterator]" property as a function.
final Object ito = ScriptRuntime.callIterator(arg1, cx, scope);
if (Undefined.instance.equals(ito)) {
// Per spec, ignore if the iterator is undefined
return false;
}

// Finally, run through all the iterated values and add them!
try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, ito)) {
for (Object val : it) {
Scriptable sVal = ScriptableObject.ensureScriptable(val);
if (sVal instanceof Symbol) {
throw ScriptRuntime.typeErrorById(
"msg.arg.not.object", ScriptRuntime.typeof(sVal));
}
Object finalKey = sVal.get(0, sVal);
if (finalKey == Scriptable.NOT_FOUND) {
finalKey = Undefined.instance;
}
Object finalVal = sVal.get(1, sVal);
if (finalVal == Scriptable.NOT_FOUND) {
finalVal = Undefined.instance;
}
setter.accept(finalKey, finalVal);
}
}
return true;
}
/**
* Prepare for calling name(...): return function corresponding to name and make current top
* scope available as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. The
Expand Down Expand Up @@ -4293,22 +4338,30 @@ public static Scriptable newObjectLiteral(
// -1 for property getter, 1 for property setter, 0 for a regular value property
int getterSetter = getterSetters == null ? 0 : getterSetters[i];
Object value = propertyValues[i];

if (getterSetter == 0) {
if (id instanceof String) {
if (isSpecialProperty((String) id)) {
Ref ref = specialRef(object, (String) id, cx, scope);
if (id instanceof Symbol) {
Symbol sym = (Symbol) id;
SymbolScriptable so = (SymbolScriptable) object;
so.put(sym, object, value);
} else if (id instanceof Integer) {
int index = ((Integer) id).intValue();
object.put(index, object, value);
} else {
String stringId = ScriptRuntime.toString(id);
if (isSpecialProperty(stringId)) {
Ref ref = specialRef(object, stringId, cx, scope);
ref.set(cx, scope, value);
} else {
object.put((String) id, object, value);
object.put(stringId, object, value);
}
} else {
int index = ((Integer) id).intValue();
object.put(index, object, value);
}
} else {
ScriptableObject so = (ScriptableObject) object;
Callable getterOrSetter = (Callable) value;
boolean isSetter = getterSetter == 1;
// XXX: Do we have to handle Symbol here.
// This will be required, when conputedprops are supported.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we need to check this. See FOCONIS@6d2a459
But I think this should be done in a separate PR

String key = id instanceof String ? (String) id : null;
int index = key == null ? ((Integer) id).intValue() : 0;
so.setGetterOrSetter(key, index, getterOrSetter, isSetter);
Expand Down
Loading