Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow freezing of NativeJavaObject and subclasses #1541

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,15 @@ static boolean setIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) {
7. both conditions under which false would be returned cannot occur here
8. both conditions under which false would be returned cannot occur here
*/
// TODO check .preventExtensions() return value once implemented and act accordingly to spec

if (o instanceof NativeJavaObject) {
((NativeJavaObject) o).freezeObject();
return true;
}

ScriptableObject obj = ScriptableObject.ensureScriptableObject(o);

// TODO check .preventExtensions() return value once implemented and act accordingly to spec
obj.preventExtensions();

for (Object key : obj.getIds(true, true)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ public void put(String id, Scriptable start, Object value) {
@Override
public void put(int index, Scriptable start, Object value) {
if (0 <= index && index < length) {
if (super.checkFrozen(Integer.toString(index)) == FrozenCheckResult.SILENTLY_IGNORE)
return;

Array.set(array, index, Context.jsToJava(value, cls));
} else {
throw Context.reportRuntimeErrorById(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public Object get(String name, Scriptable start) {

@Override
public void put(String name, Scriptable start, Object value) {
if (super.checkFrozen(name) == FrozenCheckResult.SILENTLY_IGNORE) return;
members.put(this, name, javaObject, value, true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ public Object get(Symbol key, Scriptable start) {
@Override
public void put(int index, Scriptable start, Object value) {
if (index >= 0) {
if (super.checkFrozen(Integer.toString(index)) == FrozenCheckResult.SILENTLY_IGNORE)
return;

Object javaValue = Context.jsToJava(value, Object.class);
if (index == list.size()) {
list.add(javaValue); // use "add" at the end of list.
Expand All @@ -139,6 +142,8 @@ public void put(int index, Scriptable start, Object value) {
@Override
public void put(String name, Scriptable start, Object value) {
if (list != null && "length".equals(name)) {
if (super.checkFrozen(name) == FrozenCheckResult.SILENTLY_IGNORE) return;

setLength(value);
return;
}
Expand Down
3 changes: 3 additions & 0 deletions rhino/src/main/java/org/mozilla/javascript/NativeJavaMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public Object get(Symbol key, Scriptable start) {
public void put(String name, Scriptable start, Object value) {
Context cx = Context.getCurrentContext();
if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) {
if (super.checkFrozen(name) == FrozenCheckResult.SILENTLY_IGNORE) return;
map.put(name, Context.jsToJava(value, Object.class));
} else {
super.put(name, start, value);
Expand All @@ -119,6 +120,8 @@ public void put(String name, Scriptable start, Object value) {
public void put(int index, Scriptable start, Object value) {
Context cx = Context.getContext();
if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) {
if (super.checkFrozen(Integer.toString(index)) == FrozenCheckResult.SILENTLY_IGNORE)
return;
map.put(Integer.valueOf(index), Context.jsToJava(value, Object.class));
} else {
super.put(index, start, value);
Expand Down
43 changes: 41 additions & 2 deletions rhino/src/main/java/org/mozilla/javascript/NativeJavaObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,13 @@ public void put(String name, Scriptable start, Object value) {
// We could be asked to modify the value of a property in the
// prototype. Since we can't add a property to a Java object,
// we modify it in the prototype rather than copy it down.
if (prototype == null || members.has(name, false))
if (prototype == null || members.has(name, false)) {
if (checkFrozen(name) == FrozenCheckResult.SILENTLY_IGNORE) return;

members.put(this, name, javaObject, value, false);
else prototype.put(name, prototype, value);
} else {
prototype.put(name, prototype, value);
}
}

@Override
Expand All @@ -127,6 +131,8 @@ public void put(Symbol symbol, Scriptable start, Object value) {
// we modify it in the prototype rather than copy it down.
String name = symbol.toString();
if (prototype == null || members.has(name, false)) {
if (checkFrozen(symbol.toString()) == FrozenCheckResult.SILENTLY_IGNORE) return;

members.put(this, name, javaObject, value, false);
} else if (prototype instanceof SymbolScriptable) {
((SymbolScriptable) prototype).put(symbol, prototype, value);
Expand Down Expand Up @@ -249,6 +255,38 @@ public Object getDefaultValue(Class<?> hint) {
return value;
}

/**
* Freeze this object, preventing changes to the values of existing properties. It is already
* not possible to defined new properties on this class, so we do not need to distinguish like
* {@link ScriptableObject#isExtensible} and {@link ScriptableObject#isSealed}.
*/
public void freezeObject() {
this.isFrozen = true;
}

/**
* Checks whether the object is frozen or not. Can either throw an exception (in strict mode) or
* return whether we should perform or silently ignore the assignment.
*/
protected FrozenCheckResult checkFrozen(String id) {
if (isFrozen) {
// In strict mode, we throw an error. In non-strict mode, we silently ignore the
// assignment
if (Context.isCurrentContextStrict()) {
String msg = "Assignment to \"" + id + "\" on frozen object in strict mode";
throw ScriptRuntime.constructError("ReferenceError", msg);
} else return FrozenCheckResult.SILENTLY_IGNORE;
}

// Go ahead!
return FrozenCheckResult.PERFORM_ASSIGNMENT;
}

protected enum FrozenCheckResult {
SILENTLY_IGNORE,
PERFORM_ASSIGNMENT,
}

/**
* Determine whether we can/should convert between the given type and the desired one. This
* should be superceded by a conversion-cost calculation function, but for now I'll hide behind
Expand Down Expand Up @@ -958,6 +996,7 @@ protected String getTag() {
protected Scriptable parent;

protected transient Object javaObject;
protected transient boolean isFrozen = false;

protected transient Class<?> staticType;
protected transient JavaMembers members;
Expand Down
3 changes: 2 additions & 1 deletion rhino/src/main/java/org/mozilla/javascript/NativeObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,8 @@ public Object execIdCall(
{
Object arg = args.length < 1 ? Undefined.instance : args[0];
if (cx.getLanguageVersion() >= Context.VERSION_ES6
&& !(arg instanceof ScriptableObject)) {
&& !(arg instanceof ScriptableObject)
&& !(arg instanceof NativeJavaObject)) {
return arg;
}

Expand Down
Loading
Loading