Skip to content

Commit c405fb6

Browse files
authored
chore: Start splitting up js-compute-builtins.cpp (#157)
#157
1 parent 6f58f29 commit c405fb6

File tree

7 files changed

+297
-263
lines changed

7 files changed

+297
-263
lines changed

c-dependencies/js-compute-runtime/.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
/rusturl
66
/compiler_flags
7-
/*.o
8-
/*.d
97
/*.wasm
8+
9+
*.o
10+
*.d

c-dependencies/js-compute-runtime/Makefile

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ FSM_SRC := $(ROOT_SRC)/js-compute-runtime/
2626
WASI_CXX ?= /opt/wasi-sdk/bin/clang++
2727

2828

29-
CXX_FLAGS := -std=gnu++17 -Wall -Werror -Qunused-arguments -fno-sized-deallocation -fno-aligned-new -mthread-model single -fPIC -fno-rtti -fno-exceptions -fno-math-errno -pipe -fno-omit-frame-pointer -funwind-tables
29+
CXX_FLAGS := -std=gnu++17 -Wall -Werror -Qunused-arguments -fno-sized-deallocation -fno-aligned-new -mthread-model single -fPIC -fno-rtti -fno-exceptions -fno-math-errno -pipe -fno-omit-frame-pointer -funwind-tables -I$(FSM_SRC)
3030
DEFINES ?=
3131
LD_FLAGS := -Wl,-z,noexecstack -Wl,-z,text -Wl,-z,relro -Wl,-z,nocopyreloc -Wl,-z,stack-size=1048576 -Wl,--stack-first
3232

@@ -43,7 +43,7 @@ WASM_STRIP = wasm-opt --strip-debug -o $1 $1
4343
endif
4444
endif
4545

46-
FSM_CPP := $(wildcard $(FSM_SRC)*.cpp)
46+
FSM_CPP := $(wildcard $(FSM_SRC)*.cpp) $(wildcard $(FSM_SRC)builtins/*.cpp)
4747
FSM_DEP := $(patsubst $(FSM_SRC)%.cpp,$(OBJDIR)%.d,$(FSM_CPP))
4848
FSM_OBJ := $(patsubst $(FSM_SRC)%.cpp,$(OBJDIR)%.o,$(FSM_CPP))
4949
RUST_URL_SRC := $(FSM_SRC)rust-url
@@ -59,6 +59,9 @@ $(RUST_URL_LIB): $(RUST_URL_RS_FILES) $(RUST_URL_SRC)/Cargo.toml $(RUST_URL_SRC)
5959
%.o: $(FSM_SRC)%.cpp $(FSM_SRC)Makefile $(RUST_URL_LIB) compiler_flags
6060
$(WASI_CXX) $(CXX_FLAGS) $(CXX_OPT) $(DEFINES) -I $(SM_SRC)include -MMD -MP -c -o $@ $<
6161

62+
builtins/%.o: $(FSM_SRC)builtins/%.cpp $(FSM_SRC)Makefile $(RUST_URL_LIB) compiler_flags
63+
$(WASI_CXX) $(CXX_FLAGS) $(CXX_OPT) $(DEFINES) -I $(SM_SRC)include -MMD -MP -c -o $@ $<
64+
6265
js-compute-runtime.wasm: $(FSM_OBJ) $(SM_OBJ) $(RUST_URL_LIB)
6366
$(WASI_CXX) $(CXX_FLAGS) $(CXX_OPT) $(DEFINES) $(LD_FLAGS) -o $@ $^
6467
$(call WASM_STRIP,$@)
@@ -84,7 +87,7 @@ compile_commands.json:
8487
sep=","; \
8588
echo "{ \"directory\": \"$(FSM_SRC)\","; \
8689
echo " \"command\": \"$(WASI_CXX) $(CXX_FLAGS) $(DEFINES) -I $(SM_SRC)include\","; \
87-
echo -n " \"file\": \"$$(basename $$file)\"}"; \
90+
echo -n " \"file\": \"$${file#$(FSM_SRC)}\"}"; \
8891
done; \
8992
echo; \
9093
echo ']' \
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
#ifndef JS_COMPUTE_RUNTIME_BUILTIN_H
2+
#define JS_COMPUTE_RUNTIME_BUILTIN_H
3+
4+
#include "js-compute-builtins.h"
5+
6+
/* Returns false if an exception is set on `cx` and the caller should
7+
immediately return to propagate the exception. */
8+
static inline bool handle_fastly_result(JSContext *cx, int result, int line, const char *func) {
9+
switch (result) {
10+
case 0:
11+
return true;
12+
case 1:
13+
JS_ReportErrorUTF8(cx,
14+
"%s: Generic error value. This means that some unexpected error "
15+
"occurred during a hostcall. - Fastly error code %d\n",
16+
func, result);
17+
return false;
18+
case 2:
19+
JS_ReportErrorUTF8(cx, "%s: Invalid argument. - Fastly error code %d\n", func, result);
20+
return false;
21+
case 3:
22+
JS_ReportErrorUTF8(cx,
23+
"%s: Invalid handle. Thrown when a request, response, dictionary, or "
24+
"body handle is not valid. - Fastly error code %d\n",
25+
func, result);
26+
return false;
27+
case 4:
28+
JS_ReportErrorUTF8(cx, "%s: Buffer length error. Buffer is too long. - Fastly error code %d\n",
29+
func, result);
30+
return false;
31+
case 5:
32+
JS_ReportErrorUTF8(cx,
33+
"%s: Unsupported operation error. This error is thrown "
34+
"when some operation cannot be performed, because it is "
35+
"not supported. - Fastly error code %d\n",
36+
func, result);
37+
return false;
38+
case 6:
39+
JS_ReportErrorUTF8(cx,
40+
"%s: Alignment error. This is thrown when a pointer does not point to "
41+
"a properly aligned slice of memory. - Fastly error code %d\n",
42+
func, result);
43+
return false;
44+
case 7:
45+
JS_ReportErrorUTF8(cx,
46+
"%s: HTTP parse error. This can be thrown when a method, URI, header, "
47+
"or status is not valid. This can also be thrown if a message head is "
48+
"too large. - Fastly error code %d\n",
49+
func, result);
50+
return false;
51+
case 8:
52+
JS_ReportErrorUTF8(cx,
53+
"%s: HTTP user error. This is thrown in cases where user code caused "
54+
"an HTTP error. For example, attempt to send a 1xx response code, or a "
55+
"request with a non-absolute URI. This can also be caused by an "
56+
"unexpected header: both `content-length` and `transfer-encoding`, for "
57+
"example. - Fastly error code %d\n",
58+
func, result);
59+
return false;
60+
case 9:
61+
JS_ReportErrorUTF8(cx,
62+
"%s: HTTP incomplete message error. A stream ended "
63+
"unexpectedly. - Fastly error code %d\n",
64+
func, result);
65+
return false;
66+
case 10:
67+
JS_ReportErrorUTF8(cx,
68+
"%s: A `None` error. This status code is used to "
69+
"indicate when an optional value did not exist, as "
70+
"opposed to an empty value. - Fastly error code %d\n",
71+
func, result);
72+
return false;
73+
case 11:
74+
JS_ReportErrorUTF8(cx,
75+
"%s: HTTP head too large error. This error will be thrown when the "
76+
"message head is too large. - Fastly error code %d\n",
77+
func, result);
78+
return false;
79+
case 12:
80+
JS_ReportErrorUTF8(cx,
81+
"%s: HTTP invalid status error. This error will be "
82+
"thrown when the HTTP message contains an invalid "
83+
"status code. - Fastly error code %d\n",
84+
func, result);
85+
return false;
86+
default:
87+
fprintf(stdout, __FILE__ ":%d (%s) - Fastly error code %d\n", line, func, result);
88+
JS_ReportErrorUTF8(cx, "Fastly error code %d", result);
89+
return false;
90+
}
91+
}
92+
93+
#define HANDLE_RESULT(cx, result) handle_fastly_result(cx, result, __LINE__, __func__)
94+
95+
#define DBG(...) \
96+
printf("%s#%d: ", __func__, __LINE__); \
97+
printf(__VA_ARGS__); \
98+
fflush(stdout);
99+
100+
#define MULTI_VALUE_HOSTCALL(op, accum) \
101+
uint32_t cursor = 0; \
102+
int64_t ending_cursor = 0; \
103+
size_t nwritten; \
104+
\
105+
while (true) { \
106+
op \
107+
\
108+
if (nwritten == 0) { \
109+
break; \
110+
} \
111+
\
112+
accum \
113+
\
114+
if (ending_cursor < 0) { \
115+
break; \
116+
} \
117+
\
118+
cursor = (uint32_t)ending_cursor; \
119+
}
120+
121+
#define CLASS_BOILERPLATE_CUSTOM_INIT(cls) \
122+
static constexpr const JSClassOps class_ops = {}; \
123+
static const uint32_t class_flags = 0; \
124+
\
125+
const JSClass class_ = {#cls, JSCLASS_HAS_RESERVED_SLOTS(Slots::Count) | class_flags, \
126+
&class_ops}; \
127+
static JS::PersistentRooted<JSObject *> proto_obj; \
128+
\
129+
bool is_instance(JSObject *obj) { return !!obj && JS::GetClass(obj) == &class_; } \
130+
\
131+
bool is_instance(JS::Value val) { return val.isObject() && is_instance(&val.toObject()); } \
132+
\
133+
bool check_receiver(JSContext *cx, JS::HandleValue receiver, const char *method_name) { \
134+
if (!is_instance(receiver)) { \
135+
JS_ReportErrorUTF8(cx, "Method %s called on receiver that's not an instance of %s\n", \
136+
method_name, class_.name); \
137+
return false; \
138+
} \
139+
return true; \
140+
}; \
141+
\
142+
bool init_class_impl(JSContext *cx, JS::HandleObject global, \
143+
JS::HandleObject parent_proto = nullptr) { \
144+
proto_obj.init(cx, JS_InitClass(cx, global, parent_proto, &class_, constructor, ctor_length, \
145+
properties, methods, nullptr, nullptr)); \
146+
return proto_obj; \
147+
};
148+
149+
#define CLASS_BOILERPLATE(cls) \
150+
CLASS_BOILERPLATE_CUSTOM_INIT(cls) \
151+
\
152+
bool init_class(JSContext *cx, JS::HandleObject global) { return init_class_impl(cx, global); }
153+
154+
#define CLASS_BOILERPLATE_NO_CTOR(cls) \
155+
bool constructor(JSContext *cx, unsigned argc, JS::Value *vp) { \
156+
JS_ReportErrorUTF8(cx, #cls " can't be instantiated directly"); \
157+
return false; \
158+
} \
159+
\
160+
CLASS_BOILERPLATE_CUSTOM_INIT(cls) \
161+
\
162+
bool init_class(JSContext *cx, JS::HandleObject global) { \
163+
/* Right now, deleting the ctor from the global object after class \
164+
initialization seems to be the best we can do. Not ideal, but works. */ \
165+
return init_class_impl(cx, global) && JS_DeleteProperty(cx, global, class_.name); \
166+
}
167+
168+
// Define this to make most methods print their name to stderr when invoked.
169+
// #define TRACE_METHOD_CALLS
170+
171+
#ifdef TRACE_METHOD_CALLS
172+
#define TRACE_METHOD(name) DBG("%s\n", name)
173+
#else
174+
#define TRACE_METHOD(name)
175+
#endif
176+
177+
#define METHOD_HEADER_WITH_NAME(required_argc, name) \
178+
TRACE_METHOD(name) \
179+
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
180+
if (!check_receiver(cx, args.thisv(), name)) \
181+
return false; \
182+
JS::RootedObject self(cx, &args.thisv().toObject()); \
183+
if (!args.requireAtLeast(cx, name, required_argc)) \
184+
return false;
185+
186+
#define METHOD_HEADER(required_argc) METHOD_HEADER_WITH_NAME(required_argc, __func__)
187+
188+
#define CTOR_HEADER(name, required_argc) \
189+
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
190+
if (!ThrowIfNotConstructing(cx, args, name)) { \
191+
return false; \
192+
} \
193+
if (!args.requireAtLeast(cx, name " constructor", required_argc)) { \
194+
return false; \
195+
}
196+
197+
#define REQUEST_HANDLER_ONLY(name) \
198+
if (isWizening()) { \
199+
JS_ReportErrorUTF8(cx, \
200+
"%s can only be used during request handling, " \
201+
"not during initialization", \
202+
name); \
203+
return false; \
204+
}
205+
206+
#define INIT_ONLY(name) \
207+
if (hasWizeningFinished()) { \
208+
JS_ReportErrorUTF8(cx, \
209+
"%s can only be used during initialization, " \
210+
"not during request handling", \
211+
name); \
212+
return false; \
213+
}
214+
215+
#endif
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include "logger.h"
2+
#include "xqd.h"
3+
4+
namespace Logger {
5+
namespace Slots {
6+
enum { Endpoint, Count };
7+
};
8+
9+
bool check_receiver(JSContext *cx, JS::HandleValue receiver, const char *method_name);
10+
11+
namespace {
12+
13+
const unsigned ctor_length = 1;
14+
15+
static bool log(JSContext *cx, unsigned argc, JS::Value *vp) {
16+
METHOD_HEADER(1)
17+
18+
auto endpoint = LogEndpointHandle{(uint32_t)JS::GetReservedSlot(self, Slots::Endpoint).toInt32()};
19+
20+
size_t msg_len;
21+
JS::UniqueChars msg = encode(cx, args.get(0), &msg_len);
22+
if (!msg)
23+
return false;
24+
25+
size_t nwritten;
26+
if (!HANDLE_RESULT(cx, xqd_log_write(endpoint, msg.get(), msg_len, &nwritten)))
27+
return false;
28+
29+
args.rval().setUndefined();
30+
return true;
31+
}
32+
33+
const JSFunctionSpec methods[] = {JS_FN("log", log, 1, JSPROP_ENUMERATE), JS_FS_END};
34+
35+
const JSPropertySpec properties[] = {JS_PS_END};
36+
37+
} // namespace
38+
39+
CLASS_BOILERPLATE_NO_CTOR(Logger)
40+
41+
JSObject *create(JSContext *cx, const char *name) {
42+
JS::RootedObject logger(cx, JS_NewObjectWithGivenProto(cx, &class_, proto_obj));
43+
if (!logger)
44+
return nullptr;
45+
46+
auto handle = LogEndpointHandle{INVALID_HANDLE};
47+
48+
if (!HANDLE_RESULT(cx, xqd_log_endpoint_get(name, strlen(name), &handle)))
49+
return nullptr;
50+
51+
JS::SetReservedSlot(logger, Slots::Endpoint, JS::Int32Value(handle.handle));
52+
53+
return logger;
54+
}
55+
} // namespace Logger
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#ifndef JS_COMPUTE_RUNTIME_LOGGER_H
2+
#define JS_COMPUTE_RUNTIME_LOGGER_H
3+
4+
#include "builtin.h"
5+
6+
namespace Logger {
7+
// Register the Logger class.
8+
bool init_class(JSContext *cx, JS::HandleObject global);
9+
10+
// Create an instance of the logger class.
11+
JSObject *create(JSContext *cx, const char *name);
12+
} // namespace Logger
13+
14+
#endif

0 commit comments

Comments
 (0)