mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 04:17:35 +00:00
LibJS: Add "name" property to functions
This commit is contained in:
parent
d007e8d00f
commit
99be27b4a1
16 changed files with 118 additions and 16 deletions
|
@ -51,6 +51,7 @@ void FunctionPrototype::initialize()
|
||||||
put_native_function("call", call, 1, attr);
|
put_native_function("call", call, 1, attr);
|
||||||
put_native_function("toString", to_string, 0, attr);
|
put_native_function("toString", to_string, 0, attr);
|
||||||
put("length", Value(0), Attribute::Configurable);
|
put("length", Value(0), Attribute::Configurable);
|
||||||
|
put("name", js_string(heap(), ""), Attribute::Configurable);
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionPrototype::~FunctionPrototype()
|
FunctionPrototype::~FunctionPrototype()
|
||||||
|
|
|
@ -61,6 +61,7 @@ template<typename ConstructorType>
|
||||||
void GlobalObject::add_constructor(const FlyString& property_name, ConstructorType*& constructor, Object& prototype)
|
void GlobalObject::add_constructor(const FlyString& property_name, ConstructorType*& constructor, Object& prototype)
|
||||||
{
|
{
|
||||||
constructor = heap().allocate<ConstructorType>();
|
constructor = heap().allocate<ConstructorType>();
|
||||||
|
constructor->put("name", js_string(heap(), property_name), Attribute::Configurable);
|
||||||
prototype.put("constructor", constructor);
|
prototype.put("constructor", constructor);
|
||||||
put(property_name, constructor, Attribute::Writable | Attribute::Configurable);
|
put(property_name, constructor, Attribute::Writable | Attribute::Configurable);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,9 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual void visit_children(Visitor&) override;
|
virtual void visit_children(Visitor&) override;
|
||||||
|
|
||||||
|
template<typename ConstructorType>
|
||||||
|
void add_constructor(const FlyString& property_name, ConstructorType*&, Object& prototype);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual const char* class_name() const override { return "GlobalObject"; }
|
virtual const char* class_name() const override { return "GlobalObject"; }
|
||||||
|
|
||||||
|
@ -55,9 +58,6 @@ private:
|
||||||
static Value is_nan(Interpreter&);
|
static Value is_nan(Interpreter&);
|
||||||
static Value is_finite(Interpreter&);
|
static Value is_finite(Interpreter&);
|
||||||
|
|
||||||
template<typename ConstructorType>
|
|
||||||
void add_constructor(const FlyString& property_name, ConstructorType*&, Object& prototype);
|
|
||||||
|
|
||||||
Shape* m_empty_object_shape { nullptr };
|
Shape* m_empty_object_shape { nullptr };
|
||||||
|
|
||||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
|
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
|
||||||
|
|
|
@ -397,6 +397,7 @@ bool Object::put_native_function(const FlyString& property_name, AK::Function<Va
|
||||||
{
|
{
|
||||||
auto* function = NativeFunction::create(interpreter(), interpreter().global_object(), property_name, move(native_function));
|
auto* function = NativeFunction::create(interpreter(), interpreter().global_object(), property_name, move(native_function));
|
||||||
function->put("length", Value(length), Attribute::Configurable);
|
function->put("length", Value(length), Attribute::Configurable);
|
||||||
|
function->put("name", js_string(heap(), property_name), Attribute::Configurable);
|
||||||
return put(property_name, function, attributes);
|
return put(property_name, function, attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,18 @@
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
|
static ScriptFunction* script_function_from(Interpreter& interpreter)
|
||||||
|
{
|
||||||
|
auto* this_object = interpreter.this_value().to_object(interpreter.heap());
|
||||||
|
if (!this_object)
|
||||||
|
return nullptr;
|
||||||
|
if (!this_object->is_function()) {
|
||||||
|
interpreter.throw_exception<TypeError>("Not a function");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return static_cast<ScriptFunction*>(this_object);
|
||||||
|
}
|
||||||
|
|
||||||
ScriptFunction* ScriptFunction::create(GlobalObject& global_object, const FlyString& name, const Statement& body, Vector<FlyString> parameters, LexicalEnvironment* parent_environment)
|
ScriptFunction* ScriptFunction::create(GlobalObject& global_object, const FlyString& name, const Statement& body, Vector<FlyString> parameters, LexicalEnvironment* parent_environment)
|
||||||
{
|
{
|
||||||
return global_object.heap().allocate<ScriptFunction>(name, body, move(parameters), parent_environment, *global_object.function_prototype());
|
return global_object.heap().allocate<ScriptFunction>(name, body, move(parameters), parent_environment, *global_object.function_prototype());
|
||||||
|
@ -47,7 +59,8 @@ ScriptFunction::ScriptFunction(const FlyString& name, const Statement& body, Vec
|
||||||
, m_parent_environment(parent_environment)
|
, m_parent_environment(parent_environment)
|
||||||
{
|
{
|
||||||
put("prototype", Object::create_empty(interpreter(), interpreter().global_object()), 0);
|
put("prototype", Object::create_empty(interpreter(), interpreter().global_object()), 0);
|
||||||
put_native_property("length", length_getter, length_setter, Attribute::Configurable);
|
put_native_property("length", length_getter, nullptr, Attribute::Configurable);
|
||||||
|
put_native_property("name", name_getter, nullptr, Attribute::Configurable);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptFunction::~ScriptFunction()
|
ScriptFunction::~ScriptFunction()
|
||||||
|
@ -101,16 +114,18 @@ Value ScriptFunction::construct(Interpreter& interpreter)
|
||||||
|
|
||||||
Value ScriptFunction::length_getter(Interpreter& interpreter)
|
Value ScriptFunction::length_getter(Interpreter& interpreter)
|
||||||
{
|
{
|
||||||
auto* this_object = interpreter.this_value().to_object(interpreter.heap());
|
auto* function = script_function_from(interpreter);
|
||||||
if (!this_object)
|
if (!function)
|
||||||
return {};
|
return {};
|
||||||
if (!this_object->is_function())
|
return Value(static_cast<i32>(function->parameters().size()));
|
||||||
return interpreter.throw_exception<TypeError>("Not a function");
|
|
||||||
return Value(static_cast<i32>(static_cast<const ScriptFunction*>(this_object)->parameters().size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptFunction::length_setter(Interpreter&, Value)
|
Value ScriptFunction::name_getter(Interpreter& interpreter)
|
||||||
{
|
{
|
||||||
|
auto* function = script_function_from(interpreter);
|
||||||
|
if (!function)
|
||||||
|
return {};
|
||||||
|
return js_string(interpreter, function->name().is_null() ? "" : function->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ private:
|
||||||
virtual void visit_children(Visitor&) override;
|
virtual void visit_children(Visitor&) override;
|
||||||
|
|
||||||
static Value length_getter(Interpreter&);
|
static Value length_getter(Interpreter&);
|
||||||
static void length_setter(Interpreter&, Value);
|
static Value name_getter(Interpreter&);
|
||||||
|
|
||||||
FlyString m_name;
|
FlyString m_name;
|
||||||
NonnullRefPtr<Statement> m_body;
|
NonnullRefPtr<Statement> m_body;
|
||||||
|
|
|
@ -2,15 +2,16 @@ load("test-common.js");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assert(Array.length === 1);
|
assert(Array.length === 1);
|
||||||
|
assert(Array.name === "Array");
|
||||||
assert(Array.prototype.length === 0);
|
assert(Array.prototype.length === 0);
|
||||||
|
|
||||||
assert(typeof Array() === "object");
|
assert(typeof Array() === "object");
|
||||||
assert(typeof new Array() === "object");
|
assert(typeof new Array() === "object");
|
||||||
|
|
||||||
x = new Array(5);
|
var a = new Array(5);
|
||||||
|
assert(a.length === 5);
|
||||||
assert(x.length === 5);
|
|
||||||
|
|
||||||
console.log("PASS");
|
console.log("PASS");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("FAIL: " + e.message);
|
console.log("FAIL: " + e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@ load("test-common.js");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assert(Boolean.length === 1);
|
assert(Boolean.length === 1);
|
||||||
|
assert(Boolean.name === "Boolean");
|
||||||
|
assert(Boolean.prototype.length === undefined);
|
||||||
|
|
||||||
assert(typeof new Boolean() === "object");
|
assert(typeof new Boolean() === "object");
|
||||||
assert(new Boolean().valueOf() === false);
|
assert(new Boolean().valueOf() === false);
|
||||||
|
|
||||||
|
|
11
Libraries/LibJS/Tests/Date.js
Normal file
11
Libraries/LibJS/Tests/Date.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
load("test-common.js");
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert(Date.length === 7);
|
||||||
|
assert(Date.name === "Date");
|
||||||
|
assert(Date.prototype.length === undefined);
|
||||||
|
|
||||||
|
console.log("PASS");
|
||||||
|
} catch (e) {
|
||||||
|
console.log("FAIL: " + e);
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
load("test-common.js");
|
load("test-common.js");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
assert(Error.length === 1);
|
||||||
|
assert(Error.name === "Error");
|
||||||
|
assert(Error.prototype.length === undefined);
|
||||||
|
|
||||||
var e;
|
var e;
|
||||||
|
|
||||||
e = Error();
|
e = Error();
|
||||||
|
|
|
@ -2,7 +2,9 @@ load("test-common.js");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assert(Function.length === 1);
|
assert(Function.length === 1);
|
||||||
|
assert(Function.name === "Function");
|
||||||
assert(Function.prototype.length === 0);
|
assert(Function.prototype.length === 0);
|
||||||
|
assert(Function.prototype.name === "");
|
||||||
|
|
||||||
assert(typeof Function() === "function");
|
assert(typeof Function() === "function");
|
||||||
assert(typeof new Function() === "function");
|
assert(typeof new Function() === "function");
|
||||||
|
@ -21,6 +23,7 @@ try {
|
||||||
assert(new Function("return typeof Function()")() === "function");
|
assert(new Function("return typeof Function()")() === "function");
|
||||||
assert(new Function("x", "return function (y) { return x + y };")(1)(2) === 3);
|
assert(new Function("x", "return function (y) { return x + y };")(1)(2) === 3);
|
||||||
|
|
||||||
|
assert(new Function().name === "anonymous");
|
||||||
assert(new Function().toString() === "function anonymous() {\n ???\n}");
|
assert(new Function().toString() === "function anonymous() {\n ???\n}");
|
||||||
|
|
||||||
console.log("PASS");
|
console.log("PASS");
|
||||||
|
|
|
@ -2,8 +2,12 @@ load("test-common.js");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assert(Number.length === 1);
|
assert(Number.length === 1);
|
||||||
|
assert(Number.name === "Number");
|
||||||
|
assert(Number.prototype.length === undefined);
|
||||||
|
|
||||||
assert(typeof Number() === "number");
|
assert(typeof Number() === "number");
|
||||||
assert(typeof new Number() === "object");
|
assert(typeof new Number() === "object");
|
||||||
|
|
||||||
assert(Number() === 0);
|
assert(Number() === 0);
|
||||||
assert(new Number().valueOf() === 0);
|
assert(new Number().valueOf() === 0);
|
||||||
assert(Number("42") === 42);
|
assert(Number("42") === 42);
|
||||||
|
|
14
Libraries/LibJS/Tests/Object.js
Normal file
14
Libraries/LibJS/Tests/Object.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
load("test-common.js");
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert(Object.length === 1);
|
||||||
|
assert(Object.name === "Object");
|
||||||
|
assert(Object.prototype.length === undefined);
|
||||||
|
|
||||||
|
assert(typeof Object() === "object");
|
||||||
|
assert(typeof new Object() === "object");
|
||||||
|
|
||||||
|
console.log("PASS");
|
||||||
|
} catch (e) {
|
||||||
|
console.log("FAIL: " + e);
|
||||||
|
}
|
23
Libraries/LibJS/Tests/String.js
Normal file
23
Libraries/LibJS/Tests/String.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
load("test-common.js");
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert(String.length === 1);
|
||||||
|
assert(String.name === "String");
|
||||||
|
assert(String.prototype.length === 0);
|
||||||
|
|
||||||
|
assert(typeof String() === "string");
|
||||||
|
assert(typeof new String() === "object");
|
||||||
|
|
||||||
|
assert(String() === "");
|
||||||
|
assert(new String().valueOf() === "");
|
||||||
|
assert(String("foo") === "foo");
|
||||||
|
assert(new String("foo").valueOf() === "foo");
|
||||||
|
assert(String(123) === "123");
|
||||||
|
assert(new String(123).valueOf() === "123");
|
||||||
|
assert(String(123) === "123");
|
||||||
|
assert(new String(123).valueOf() === "123");
|
||||||
|
|
||||||
|
console.log("PASS");
|
||||||
|
} catch (e) {
|
||||||
|
console.log("FAIL: " + e);
|
||||||
|
}
|
21
Libraries/LibJS/Tests/function-name.js
Normal file
21
Libraries/LibJS/Tests/function-name.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
load("test-common.js");
|
||||||
|
|
||||||
|
try {
|
||||||
|
var f = function () { }
|
||||||
|
assert(f.name === "");
|
||||||
|
assert((f.name = "f") === "f");
|
||||||
|
assert(f.name === "");
|
||||||
|
|
||||||
|
function foo() { }
|
||||||
|
assert(foo.name === "foo");
|
||||||
|
assert((foo.name = "bar") === "bar");
|
||||||
|
assert(foo.name === "foo");
|
||||||
|
|
||||||
|
assert(console.log.name === "log");
|
||||||
|
assert((console.log.name = "warn") === "warn");
|
||||||
|
assert(console.log.name === "log");
|
||||||
|
|
||||||
|
console.log("PASS");
|
||||||
|
} catch (e) {
|
||||||
|
console.log("FAIL: " + e);
|
||||||
|
}
|
|
@ -64,7 +64,7 @@ void WindowObject::initialize()
|
||||||
m_xhr_prototype = heap().allocate<XMLHttpRequestPrototype>();
|
m_xhr_prototype = heap().allocate<XMLHttpRequestPrototype>();
|
||||||
m_xhr_constructor = heap().allocate<XMLHttpRequestConstructor>();
|
m_xhr_constructor = heap().allocate<XMLHttpRequestConstructor>();
|
||||||
m_xhr_constructor->put("prototype", m_xhr_prototype, 0);
|
m_xhr_constructor->put("prototype", m_xhr_prototype, 0);
|
||||||
put("XMLHttpRequest", m_xhr_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
|
add_constructor("XMLHttpRequest", m_xhr_constructor, *m_xhr_prototype);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowObject::~WindowObject()
|
WindowObject::~WindowObject()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue