Skip to content

Commit a481b9f

Browse files
authored
chore: Split more builtins out of js-compute-builtins.cpp (#430)
Finish splitting out the remaining builtins from js-compute-builtins.cpp, and remove the BOILERPLATE macros from builtin.h.
1 parent 334c28b commit a481b9f

21 files changed

+4652
-4185
lines changed

c-dependencies/js-compute-runtime/builtin.h

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -67,53 +67,6 @@ const JSErrorFormatString *GetErrorMessageBuiltin(void *userRef, unsigned errorN
6767
cursor = (uint32_t)ending_cursor; \
6868
}
6969

70-
#define CLASS_BOILERPLATE_CUSTOM_INIT(cls) \
71-
constexpr const JSClassOps class_ops = {}; \
72-
const uint32_t class_flags = 0; \
73-
\
74-
const JSClass class_ = {#cls, JSCLASS_HAS_RESERVED_SLOTS(Slots::Count) | class_flags, \
75-
&class_ops}; \
76-
JS::PersistentRooted<JSObject *> proto_obj; \
77-
\
78-
bool is_instance(JSObject *obj) { return !!obj && JS::GetClass(obj) == &class_; } \
79-
\
80-
bool is_instance(JS::Value val) { return val.isObject() && is_instance(&val.toObject()); } \
81-
\
82-
bool check_receiver(JSContext *cx, JS::HandleValue receiver, const char *method_name) { \
83-
if (!is_instance(receiver)) { \
84-
JS_ReportErrorNumberASCII(cx, GetErrorMessageBuiltin, nullptr, JSMSG_INCOMPATIBLE_INSTANCE, \
85-
method_name, class_.name); \
86-
return false; \
87-
} \
88-
return true; \
89-
}; \
90-
\
91-
bool init_class_impl(JSContext *cx, JS::HandleObject global, \
92-
JS::HandleObject parent_proto = nullptr) { \
93-
proto_obj.init(cx, JS_InitClass(cx, global, &class_, parent_proto, #cls, constructor, \
94-
ctor_length, properties, methods, nullptr, nullptr)); \
95-
return proto_obj; \
96-
};
97-
98-
#define CLASS_BOILERPLATE(cls) \
99-
CLASS_BOILERPLATE_CUSTOM_INIT(cls) \
100-
\
101-
bool init_class(JSContext *cx, JS::HandleObject global) { return init_class_impl(cx, global); }
102-
103-
#define CLASS_BOILERPLATE_NO_CTOR(cls) \
104-
bool constructor(JSContext *cx, unsigned argc, JS::Value *vp) { \
105-
JS_ReportErrorUTF8(cx, #cls " can't be instantiated directly"); \
106-
return false; \
107-
} \
108-
\
109-
CLASS_BOILERPLATE_CUSTOM_INIT(cls) \
110-
\
111-
bool init_class(JSContext *cx, JS::HandleObject global) { \
112-
/* Right now, deleting the ctor from the global object after class \
113-
initialization seems to be the best we can do. Not ideal, but works. */ \
114-
return init_class_impl(cx, global) && JS_DeleteProperty(cx, global, class_.name); \
115-
}
116-
11770
// Define this to make most methods print their name to stderr when invoked.
11871
// #define TRACE_METHOD_CALLS
11972

@@ -181,7 +134,7 @@ template <typename Impl> class BuiltinImpl {
181134
public:
182135
static constexpr JSClass class_{
183136
Impl::class_name,
184-
JSCLASS_HAS_RESERVED_SLOTS(Impl::Slots::Count) | class_flags,
137+
JSCLASS_HAS_RESERVED_SLOTS(static_cast<uint32_t>(Impl::Slots::Count)) | class_flags,
185138
&class_ops,
186139
};
187140

c-dependencies/js-compute-runtime/builtins/backend.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
#include "js/experimental/TypedData.h"
1818
#pragma clang diagnostic pop
1919

20-
#include "backend.h"
20+
#include "builtins/backend.h"
21+
#include "builtins/request-response.h"
2122
#include "host_call.h"
2223
#include "js-compute-builtins.h"
2324
#include "js/Conversions.h"
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#include "builtins/client-info.h"
2+
#include "geo_ip.h"
3+
#include "xqd-world/xqd_world_adapter.h"
4+
5+
#include "js/JSON.h"
6+
#include <arpa/inet.h>
7+
8+
namespace builtins {
9+
10+
namespace {
11+
12+
JSString *address(JSObject *obj) {
13+
JS::Value val = JS::GetReservedSlot(obj, static_cast<uint32_t>(ClientInfo::Slots::Address));
14+
return val.isString() ? val.toString() : nullptr;
15+
}
16+
17+
JSString *geo_info(JSObject *obj) {
18+
JS::Value val = JS::GetReservedSlot(obj, static_cast<uint32_t>(ClientInfo::Slots::GeoInfo));
19+
return val.isString() ? val.toString() : nullptr;
20+
}
21+
22+
static JSString *retrieve_address(JSContext *cx, JS::HandleObject self) {
23+
JS::RootedString address(cx);
24+
25+
fastly_list_u8_t octets;
26+
fastly_error_t err;
27+
if (!xqd_fastly_http_req_downstream_client_ip_addr(&octets, &err)) {
28+
HANDLE_ERROR(cx, err);
29+
return nullptr;
30+
}
31+
32+
switch (octets.len) {
33+
case 0: {
34+
// No address to be had, leave `address` as a nullptr.
35+
JS_free(cx, octets.ptr);
36+
break;
37+
}
38+
case 4: {
39+
char address_chars[INET_ADDRSTRLEN];
40+
// TODO: do we need to do error handling here, or can we depend on the
41+
// host giving us a valid address?
42+
inet_ntop(AF_INET, octets.ptr, address_chars, INET_ADDRSTRLEN);
43+
address = JS_NewStringCopyZ(cx, address_chars);
44+
JS_free(cx, octets.ptr);
45+
if (!address)
46+
return nullptr;
47+
48+
break;
49+
}
50+
case 16: {
51+
char address_chars[INET6_ADDRSTRLEN];
52+
// TODO: do we need to do error handling here, or can we depend on the
53+
// host giving us a valid address?
54+
inet_ntop(AF_INET6, octets.ptr, address_chars, INET6_ADDRSTRLEN);
55+
address = JS_NewStringCopyZ(cx, address_chars);
56+
JS_free(cx, octets.ptr);
57+
if (!address)
58+
return nullptr;
59+
60+
break;
61+
}
62+
}
63+
64+
JS::SetReservedSlot(self, static_cast<uint32_t>(ClientInfo::Slots::Address),
65+
JS::StringValue(address));
66+
return address;
67+
}
68+
69+
JSString *retrieve_geo_info(JSContext *cx, JS::HandleObject self) {
70+
JS::RootedString address_str(cx, address(self));
71+
if (!address_str) {
72+
address_str = retrieve_address(cx, self);
73+
if (!address_str)
74+
return nullptr;
75+
}
76+
77+
JS::RootedString geo(cx, get_geo_info(cx, address_str));
78+
if (!geo)
79+
return nullptr;
80+
81+
JS::SetReservedSlot(self, static_cast<uint32_t>(ClientInfo::Slots::GeoInfo),
82+
JS::StringValue(geo));
83+
return geo;
84+
}
85+
86+
} // namespace
87+
88+
bool ClientInfo::address_get(JSContext *cx, unsigned argc, JS::Value *vp) {
89+
METHOD_HEADER(0)
90+
91+
JS::RootedString address_str(cx, address(self));
92+
if (!address_str) {
93+
address_str = retrieve_address(cx, self);
94+
if (!address_str)
95+
return false;
96+
}
97+
98+
args.rval().setString(address_str);
99+
return true;
100+
}
101+
102+
bool ClientInfo::geo_get(JSContext *cx, unsigned argc, JS::Value *vp) {
103+
METHOD_HEADER(0)
104+
105+
JS::RootedString geo_info_str(cx, geo_info(self));
106+
if (!geo_info_str) {
107+
geo_info_str = retrieve_geo_info(cx, self);
108+
if (!geo_info_str)
109+
return false;
110+
}
111+
112+
return JS_ParseJSON(cx, geo_info_str, args.rval());
113+
}
114+
115+
const JSFunctionSpec ClientInfo::methods[] = {
116+
JS_FS_END,
117+
};
118+
119+
const JSPropertySpec ClientInfo::properties[] = {
120+
JS_PSG("address", address_get, JSPROP_ENUMERATE),
121+
JS_PSG("geo", geo_get, JSPROP_ENUMERATE),
122+
JS_PS_END,
123+
};
124+
125+
JSObject *ClientInfo::create(JSContext *cx) {
126+
return JS_NewObjectWithGivenProto(cx, &class_, proto_obj);
127+
}
128+
129+
} // namespace builtins
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#ifndef BUILTIN_CLIENT_INFO_H
2+
#define BUILTIN_CLIENT_INFO_H
3+
4+
#include "builtin.h"
5+
6+
namespace builtins {
7+
8+
class ClientInfo final : public BuiltinNoConstructor<ClientInfo> {
9+
static bool address_get(JSContext *cx, unsigned argc, JS::Value *vp);
10+
static bool geo_get(JSContext *cx, unsigned argc, JS::Value *vp);
11+
12+
public:
13+
static constexpr const char *class_name = "FetchEvent";
14+
15+
enum class Slots {
16+
Address,
17+
GeoInfo,
18+
Count,
19+
};
20+
21+
static const JSFunctionSpec methods[];
22+
static const JSPropertySpec properties[];
23+
24+
static JSObject *create(JSContext *cx);
25+
};
26+
27+
} // namespace builtins
28+
29+
#endif

0 commit comments

Comments
 (0)