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

Function prototype properties length and name are configurable (in ES6) #1284

Merged
merged 5 commits into from
Nov 30, 2022
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
2 changes: 1 addition & 1 deletion src/org/mozilla/javascript/ArrowFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public ArrowFunction(
this.targetFunction = targetFunction;
this.boundThis = boundThis;

ScriptRuntime.setFunctionProtoAndParent(this, scope);
ScriptRuntime.setFunctionProtoAndParent(this, cx, scope, false);

Function thrower = ScriptRuntime.typeErrorThrower(cx);
NativeObject throwing = new NativeObject();
Expand Down
11 changes: 10 additions & 1 deletion src/org/mozilla/javascript/BaseFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,22 @@ public class BaseFunction extends IdScriptableObject implements Function {
private static final String FUNCTION_CLASS = "Function";
static final String GENERATOR_FUNCTION_CLASS = "__GeneratorFunction";

static void init(Scriptable scope, boolean sealed) {
static void init(Context cx, Scriptable scope, boolean sealed) {
BaseFunction obj = new BaseFunction();
// Function.prototype attributes: see ECMA 15.3.3.1
obj.prototypePropertyAttributes = DONTENUM | READONLY | PERMANENT;
if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
obj.setStandardPropertyAttributes(READONLY | DONTENUM);
}
obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
}

/** @deprecated Use {@link #init(Context, Scriptable, boolean)} instead */
@Deprecated
static void init(Scriptable scope, boolean sealed) {
init(Context.getContext(), scope, sealed);
}

static Object initAsGeneratorFunction(Scriptable scope, boolean sealed) {
BaseFunction obj = new BaseFunction(true);
// Function.prototype attributes: see ECMA 15.3.3.1
Expand Down
2 changes: 1 addition & 1 deletion src/org/mozilla/javascript/BoundFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public BoundFunction(
length = 0;
}

ScriptRuntime.setFunctionProtoAndParent(this, scope);
ScriptRuntime.setFunctionProtoAndParent(this, cx, scope, false);

Function thrower = ScriptRuntime.typeErrorThrower(cx);
NativeObject throwing = new NativeObject();
Expand Down
2 changes: 1 addition & 1 deletion src/org/mozilla/javascript/FunctionObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public FunctionObject(String name, Member methodOrConstructor, Scriptable scope)
}
}

ScriptRuntime.setFunctionProtoAndParent(this, scope);
ScriptRuntime.setFunctionProtoAndParent(this, Context.getCurrentContext(), scope, false);
}

/**
Expand Down
7 changes: 4 additions & 3 deletions src/org/mozilla/javascript/JavaMembers.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class JavaMembers {
this.staticMembers = new HashMap<String, Object>();
this.cl = cl;
boolean includePrivate = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS);
reflect(scope, includeProtected, includePrivate);
reflect(cx, scope, includeProtected, includePrivate);
} finally {
Context.exit();
}
Expand Down Expand Up @@ -412,7 +412,8 @@ public int hashCode() {
}
}

private void reflect(Scriptable scope, boolean includeProtected, boolean includePrivate) {
private void reflect(
Context cx, Scriptable scope, boolean includeProtected, boolean includePrivate) {
// We reflect methods first, because we want overloaded field/method
// names to be allocated to the NativeJavaMethod before the field
// gets in the way.
Expand Down Expand Up @@ -465,7 +466,7 @@ private void reflect(Scriptable scope, boolean includeProtected, boolean include
}
NativeJavaMethod fun = new NativeJavaMethod(methodBoxes);
if (scope != null) {
ScriptRuntime.setFunctionProtoAndParent(fun, scope);
ScriptRuntime.setFunctionProtoAndParent(fun, cx, scope, false);
}
ht.put(entry.getKey(), fun);
}
Expand Down
2 changes: 1 addition & 1 deletion src/org/mozilla/javascript/NativeFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public final void initScriptFunction(Context cx, Scriptable scope) {

public final void initScriptFunction(
Context cx, Scriptable scope, boolean es6GeneratorFunction) {
ScriptRuntime.setFunctionProtoAndParent(this, scope, es6GeneratorFunction);
ScriptRuntime.setFunctionProtoAndParent(this, cx, scope, es6GeneratorFunction);
}

/**
Expand Down
8 changes: 7 additions & 1 deletion src/org/mozilla/javascript/NativeScript.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ class NativeScript extends BaseFunction {

private static final Object SCRIPT_TAG = "Script";

static void init(Scriptable scope, boolean sealed) {
static void init(Context cx, Scriptable scope, boolean sealed) {
NativeScript obj = new NativeScript(null);
obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
}

/** @deprecated Use {@link #init(Context, Scriptable, boolean)} instead */
@Deprecated
static void init(Scriptable scope, boolean sealed) {
init(Context.getContext(), scope, sealed);
}

private NativeScript(Script script) {
this.script = script;
}
Expand Down
15 changes: 12 additions & 3 deletions src/org/mozilla/javascript/ScriptRuntime.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public int getLength() {
return 0;
}
};
ScriptRuntime.setFunctionProtoAndParent(thrower, cx.topCallScope);
ScriptRuntime.setFunctionProtoAndParent(thrower, cx, cx.topCallScope, false);
thrower.preventExtensions();
cx.typeErrorThrower = thrower;
}
Expand Down Expand Up @@ -146,7 +146,7 @@ public static ScriptableObject initSafeStandardObjects(
scope.associateValue(LIBRARY_SCOPE_KEY, scope);
(new ClassCache()).associate(scope);

BaseFunction.init(scope, sealed);
BaseFunction.init(cx, scope, sealed);
NativeObject.init(scope, sealed);

Scriptable objectProto = ScriptableObject.getObjectPrototype(scope);
Expand Down Expand Up @@ -178,7 +178,7 @@ public static ScriptableObject initSafeStandardObjects(

NativeWith.init(scope, sealed);
NativeCall.init(scope, sealed);
NativeScript.init(scope, sealed);
NativeScript.init(cx, scope, sealed);

NativeIterator.init(cx, scope, sealed); // Also initializes NativeGenerator & ES6Generator

Expand Down Expand Up @@ -4263,6 +4263,15 @@ public static void setFunctionProtoAndParent(
}
}

public static void setFunctionProtoAndParent(
BaseFunction fn, Context cx, Scriptable scope, boolean es6GeneratorFunction) {
setFunctionProtoAndParent(fn, scope, es6GeneratorFunction);

if (cx != null && cx.getLanguageVersion() >= Context.VERSION_ES6) {
fn.setStandardPropertyAttributes(ScriptableObject.READONLY | ScriptableObject.DONTENUM);
}
}

public static void setObjectProtoAndParent(ScriptableObject object, Scriptable scope) {
// Compared with function it always sets the scope to top scope
scope = ScriptableObject.getTopLevelScope(scope);
Expand Down
127 changes: 127 additions & 0 deletions testsrc/org/mozilla/javascript/tests/es6/NativeFunctionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package org.mozilla.javascript.tests.es6;

import static org.junit.Assert.assertEquals;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.annotations.JSConstructor;
import org.mozilla.javascript.annotations.JSFunction;

public class NativeFunctionTest {
private Context cx;
private ScriptableObject scope;

@Before
public void setUp() throws Exception {
cx = Context.enter();
cx.setLanguageVersion(Context.VERSION_ES6);
scope = cx.initStandardObjects();
ScriptableObject.defineClass(scope, HelperObject.class);
}

@After
public void tearDown() {
Context.exit();
}

@Test
public void testFunctionPrototypeLength() {
Object result =
cx.evaluateString(
scope,
"var desc=Object.getOwnPropertyDescriptor(Function.prototype, 'length');\n"
+ "var res = 'configurable: ' + desc.configurable;\n"
+ "res += ' enumerable: ' + desc.enumerable;\n"
+ "res += ' writable: ' + desc.writable;",
"test",
1,
null);
assertEquals("configurable: true enumerable: false writable: false", result);
}

@Test
public void testFunctionPrototypeName() {
Object result =
cx.evaluateString(
scope,
"var desc=Object.getOwnPropertyDescriptor(Function.prototype, 'name');\n"
+ "var res = 'configurable: ' + desc.configurable;\n"
+ "res += ' enumerable: ' + desc.enumerable;\n"
+ "res += ' writable: ' + desc.writable;",
"test",
1,
null);
assertEquals("configurable: true enumerable: false writable: false", result);
}

@Test
public void testFunctionLength() {
Object result =
cx.evaluateString(
scope,
"var f=function(){};\n"
+ "var desc=Object.getOwnPropertyDescriptor(f, 'length');\n"
+ "var res = 'configurable: ' + desc.configurable;\n"
+ "res += ' enumerable: ' + desc.enumerable;\n"
+ "res += ' writable: ' + desc.writable;",
"test",
1,
null);
assertEquals("configurable: true enumerable: false writable: false", result);
}

@Test
public void testFunctionName() {
Object result =
cx.evaluateString(
scope,
"var f=function(){};\n"
+ "var desc=Object.getOwnPropertyDescriptor(f, 'name');\n"
+ "var res = 'configurable: ' + desc.configurable;\n"
+ "res += ' enumerable: ' + desc.enumerable;\n"
+ "res += ' writable: ' + desc.writable;",
"test",
1,
null);
assertEquals("configurable: true enumerable: false writable: false", result);
}

@Test
public void testFunctionNameJavaObject() {
Object result =
cx.evaluateString(
scope,
"var f=new HelperObject().foo;\n"
+ "var desc=Object.getOwnPropertyDescriptor(f, 'name');\n"
+ "var res = 'configurable: ' + desc.configurable;\n"
+ "res += ' enumerable: ' + desc.enumerable;\n"
+ "res += ' writable: ' + desc.writable;",
"test",
1,
null);
assertEquals("configurable: true enumerable: false writable: false", result);
}

public static class HelperObject extends ScriptableObject {

public HelperObject() {}

@Override
public String getClassName() {
return "HelperObject";
}

@JSConstructor
public void jsConstructorMethod() {
put("initialized", this, Boolean.TRUE);
}

@JSFunction("foo")
public Object foo() {
return "foo()";
}
}
}
Loading