Skip to content
File

Blob: src/workerd/api/rtti.c++

13.3 KB
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#include "rtti.h"
6 
7#include <workerd/api/actor-state.h>
8#include <workerd/api/actor.h>
9#include <workerd/api/analytics-engine.h>
10#include <workerd/api/base64.h>
11#include <workerd/api/cache.h>
12#include <workerd/api/crypto/crypto.h>
13#include <workerd/api/encoding.h>
14#include <workerd/api/events.h>
15#include <workerd/api/eventsource.h>
16#include <workerd/api/export-loopback.h>
17#include <workerd/api/filesystem.h>
18#include <workerd/api/global-scope.h>
19#include <workerd/api/html-rewriter.h>
20#include <workerd/api/hyperdrive.h>
21#include <workerd/api/kv.h>
22#include <workerd/api/memory-cache.h>
23#include <workerd/api/messagechannel.h>
24#include <workerd/api/modules.h>
25#include <workerd/api/node/node.h>
26#include <workerd/api/performance.h>
27#include <workerd/api/pyodide/pyodide.h>
28#include <workerd/api/queue.h>
29#include <workerd/api/r2.h>
30#include <workerd/api/scheduled.h>
31#include <workerd/api/sockets.h>
32#include <workerd/api/sql.h>
33#include <workerd/api/streams.h>
34#include <workerd/api/streams/standard.h>
35#include <workerd/api/sync-kv.h>
36#include <workerd/api/trace.h>
37#include <workerd/api/tracing.h>
38#include <workerd/api/unsafe.h>
39#include <workerd/api/url-standard.h>
40#include <workerd/api/urlpattern-standard.h>
41#include <workerd/api/urlpattern.h>
42#include <workerd/api/worker-loader.h>
43#include <workerd/api/worker-rpc.h>
44#include <workerd/api/workers-module.h>
45#include <workerd/io/compatibility-date.h>
46#include <workerd/jsg/modules.capnp.h>
47#include <workerd/jsg/rtti.h>
48 
49#include <kj/vector.h>
50 
51#define EW_TYPE_GROUP_FOR_EACH(F) \
52 F("dom-exception", jsg::DOMException) \
53 F("global-scope", EW_GLOBAL_SCOPE_ISOLATE_TYPES) \
54 F("durable-objects", EW_ACTOR_ISOLATE_TYPES) \
55 F("durable-objects-state", EW_ACTOR_STATE_ISOLATE_TYPES) \
56 F("analytics-engine", EW_ANALYTICS_ENGINE_ISOLATE_TYPES) \
57 F("basics", EW_BASICS_ISOLATE_TYPES) \
58 F("blob", EW_BLOB_ISOLATE_TYPES) \
59 F("cache", EW_CACHE_ISOLATE_TYPES) \
60 F("crypto", EW_CRYPTO_ISOLATE_TYPES) \
61 F("encoding", EW_ENCODING_ISOLATE_TYPES) \
62 F("events", EW_EVENTS_ISOLATE_TYPES) \
63 F("form-data", EW_FORMDATA_ISOLATE_TYPES) \
64 F("html-rewriter", EW_HTML_REWRITER_ISOLATE_TYPES) \
65 F("http", EW_HTTP_ISOLATE_TYPES) \
66 F("hyperdrive", EW_HYPERDRIVE_ISOLATE_TYPES) \
67 F("unsafe", EW_UNSAFE_ISOLATE_TYPES) \
68 F("memory-cache", EW_MEMORY_CACHE_ISOLATE_TYPES) \
69 F("pyodide", EW_PYODIDE_ISOLATE_TYPES) \
70 F("kv", EW_KV_ISOLATE_TYPES) \
71 F("queue", EW_QUEUE_ISOLATE_TYPES) \
72 F("r2", EW_R2_PUBLIC_BETA_ISOLATE_TYPES) \
73 F("worker-rpc", EW_WORKER_RPC_ISOLATE_TYPES) \
74 F("scheduled", EW_SCHEDULED_ISOLATE_TYPES) \
75 F("streams", EW_STREAMS_ISOLATE_TYPES) \
76 F("trace", EW_TRACE_ISOLATE_TYPES) \
77 F("url", EW_URL_ISOLATE_TYPES) \
78 F("url-standard", EW_URL_STANDARD_ISOLATE_TYPES) \
79 F("url-pattern", EW_URLPATTERN_ISOLATE_TYPES) \
80 F("url-pattern-standard", EW_URLPATTERN_STANDARD_ISOLATE_TYPES) \
81 F("websocket", EW_WEBSOCKET_ISOLATE_TYPES) \
82 F("sql", EW_SQL_ISOLATE_TYPES) \
83 F("sockets", EW_SOCKETS_ISOLATE_TYPES) \
84 F("base64", EW_BASE64_ISOLATE_TYPES) \
85 F("node", EW_NODE_ISOLATE_TYPES) \
86 F("rtti", EW_RTTI_ISOLATE_TYPES) \
87 F("eventsource", EW_EVENTSOURCE_ISOLATE_TYPES) \
88 F("container", EW_CONTAINER_ISOLATE_TYPES) \
89 F("webfs", EW_WEB_FILESYSTEM_ISOLATE_TYPE) \
90 F("messagechannel", EW_MESSAGECHANNEL_ISOLATE_TYPES) \
91 F("workers-module", EW_WORKERS_MODULE_ISOLATE_TYPES) \
92 F("export-loopback", EW_EXPORT_LOOPBACK_ISOLATE_TYPES) \
93 F("sync-kv", EW_SYNC_KV_ISOLATE_TYPES) \
94 F("worker-loader", EW_WORKER_LOADER_ISOLATE_TYPES) \
95 F("performance", EW_PERFORMANCE_ISOLATE_TYPES) \
96 F("tracing", EW_TRACING_ISOLATE_TYPES)
97 
98namespace workerd::api {
99 
100namespace {
101 
102struct EncoderModuleRegistryImpl {
103 struct CppModuleContents {
104 CppModuleContents(kj::String structureName): structureName(kj::mv(structureName)) {}
105 
106 kj::String structureName;
107 };
108 struct TypeScriptModuleContents {
109 TypeScriptModuleContents(kj::StringPtr tsDeclarations): tsDeclarations(tsDeclarations) {}
110 
111 kj::StringPtr tsDeclarations;
112 };
113 struct ModuleInfo {
114 ModuleInfo(kj::StringPtr specifier,
115 jsg::ModuleType type,
116 kj::OneOf<CppModuleContents, TypeScriptModuleContents> contents)
117 : specifier(specifier),
118 type(type),
119 contents(kj::mv(contents)) {}
120 
121 kj::StringPtr specifier;
122 jsg::ModuleType type;
123 kj::OneOf<CppModuleContents, TypeScriptModuleContents> contents;
124 };
125 
126 void addBuiltinBundle(
127 jsg::Bundle::Reader bundle, kj::Maybe<jsg::ModuleRegistry::Type> maybeFilter = kj::none) {
128 for (auto module: bundle.getModules()) {
129 if (module.getType() == maybeFilter.orDefault(module.getType())) addBuiltinModule(module);
130 }
131 }
132 
133 template <typename Func>
134 void addBuiltinBundleFiltered(jsg::Bundle::Reader bundle, Func filter) {
135 for (auto module: bundle.getModules()) {
136 if (filter(module)) {
137 addBuiltinModule(module);
138 }
139 }
140 }
141 
142 void addBuiltinModule(jsg::Module::Reader module) {
143 TypeScriptModuleContents contents(module.getTsDeclaration());
144 ModuleInfo info(module.getName(), module.getType(), kj::mv(contents));
145 modules.add(kj::mv(info));
146 }
147 
148 void addBuiltinModule(kj::StringPtr specifier,
149 jsg::ModuleRegistry::ModuleCallback callback,
150 jsg::ModuleRegistry::Type type = jsg::ModuleRegistry::Type::BUILTIN) {
151 // TODO(soon): Implement this function
152 }
153 
154 template <typename T>
155 void addBuiltinModule(kj::StringPtr specifier,
156 jsg::ModuleRegistry::Type type = jsg::ModuleRegistry::Type::BUILTIN) {
157 auto structureName = jsg::fullyQualifiedTypeName(typeid(T));
158 CppModuleContents contents(kj::mv(structureName));
159 ModuleInfo info(specifier, type, kj::mv(contents));
160 modules.add(kj::mv(info));
161 }
162 
163 kj::Vector<ModuleInfo> modules;
164};
165 
166CompatibilityFlags::Reader compileFlags(capnp::MessageBuilder &message,
167 kj::StringPtr compatDate,
168 bool experimental,
169 kj::ArrayPtr<kj::String> compatFlags) {
170 // Based on src/workerd/io/compatibility-date-test.c++
171 auto orphanage = message.getOrphanage();
172 auto flagListOrphan = orphanage.newOrphan<capnp::List<capnp::Text>>(compatFlags.size());
173 auto flagList = flagListOrphan.get();
174 for (auto i: kj::indices(compatFlags)) {
175 flagList.set(i, compatFlags.begin()[i]);
176 }
177 
178 auto output = message.initRoot<CompatibilityFlags>();
179 SimpleWorkerErrorReporter errorReporter;
180 
181 compileCompatibilityFlags(compatDate, flagList.asReader(), output, errorReporter, experimental,
182 CompatibilityDateValidation::FUTURE_FOR_TEST);
183 
184 if (!errorReporter.errors.empty()) {
185 // TODO(someday): throw an `AggregateError` containing all errors
186 JSG_FAIL_REQUIRE(Error, errorReporter.errors[0]);
187 }
188 
189 auto reader = output.asReader();
190 return kj::mv(reader);
191}
192 
193CompatibilityFlags::Reader compileAllFlags(capnp::MessageBuilder &message) {
194 auto output = message.initRoot<CompatibilityFlags>();
195 auto schema = capnp::Schema::from<CompatibilityFlags>();
196 auto dynamicOutput = capnp::toDynamic(output);
197 for (auto field: schema.getFields()) {
198 bool isNode = false;
199 
200 kj::StringPtr enableFlagName;
201 
202 for (auto annotation: field.getProto().getAnnotations()) {
203 if (annotation.getId() == COMPAT_ENABLE_FLAG_ANNOTATION_ID) {
204 enableFlagName = annotation.getValue().getText();
205 // Exclude nodejs_compat, since the type generation scripts don't support node:* imports
206 // TODO: Figure out typing for node compat
207 isNode = enableFlagName == "nodejs_compat" || enableFlagName == "nodejs_compat_v2";
208 }
209 }
210 
211 dynamicOutput.set(field, !isNode);
212 }
213 auto reader = output.asReader();
214 return kj::mv(reader);
215}
216 
217struct TypesEncoder {
218 public:
219 TypesEncoder(): compatFlags(kj::heapArray<kj::String>(0)) {}
220 TypesEncoder(kj::String compatDate, kj::Array<kj::String> compatFlags)
221 : compatDate(kj::mv(compatDate)),
222 compatFlags(kj::mv(compatFlags)) {}
223 
224 kj::Array<byte> encode() {
225 capnp::MallocMessageBuilder flagsMessage;
226 CompatibilityFlags::Reader flags;
227 KJ_IF_SOME(date, compatDate) {
228 flags = compileFlags(flagsMessage, date, true, compatFlags);
229 } else {
230 flags = compileAllFlags(flagsMessage);
231 }
232 capnp::MallocMessageBuilder message;
233 auto root = message.initRoot<jsg::rtti::StructureGroups>();
234 
235 // Encode RTTI structures
236 auto builder = jsg::rtti::Builder(flags);
237 
238#define EW_TYPE_GROUP_COUNT(Name, Types) groupsSize++;
239#define EW_TYPE_GROUP_WRITE(Name, Types) writeGroup<Types>(groups, builder, Name);
240 
241 unsigned int groupsSize = 0;
242 EW_TYPE_GROUP_FOR_EACH(EW_TYPE_GROUP_COUNT)
243 auto groups = root.initGroups(groupsSize);
244 groupsIndex = 0;
245 EW_TYPE_GROUP_FOR_EACH(EW_TYPE_GROUP_WRITE)
246 KJ_ASSERT(groupsIndex == groupsSize);
247 
248#undef EW_TYPE_GROUP_COUNT
249#undef EW_TYPE_GROUP_WRITE
250 
251 // Encode modules
252 EncoderModuleRegistryImpl registry;
253 registerModules(registry, flags);
254 
255 unsigned int i = 0;
256 auto modulesBuilder = root.initModules(registry.modules.size());
257 for (auto moduleBuilder: modulesBuilder) {
258 auto &module = registry.modules[i++];
259 moduleBuilder.setSpecifier(module.specifier);
260 KJ_SWITCH_ONEOF(module.contents) {
261 KJ_CASE_ONEOF(contents, EncoderModuleRegistryImpl::CppModuleContents) {
262 moduleBuilder.setStructureName(contents.structureName);
263 }
264 KJ_CASE_ONEOF(contents, EncoderModuleRegistryImpl::TypeScriptModuleContents) {
265 moduleBuilder.setTsDeclarations(contents.tsDeclarations);
266 }
267 }
268 }
269 
270 auto words = capnp::messageToFlatArray(message);
271 auto bytes = words.asBytes();
272 return kj::heapArray(bytes);
273 }
274 
275 private:
276 template <typename Type>
277 void writeStructure(jsg::rtti::Builder<CompatibilityFlags::Reader> &builder,
278 capnp::List<jsg::rtti::Structure>::Builder structures) {
279 auto reader = builder.structure<Type>();
280 structures.setWithCaveats(structureIndex++, reader);
281 }
282 
283 template <typename... Types>
284 void writeGroup(capnp::List<jsg::rtti::StructureGroups::StructureGroup>::Builder &groups,
285 jsg::rtti::Builder<CompatibilityFlags::Reader> &builder,
286 kj::StringPtr name) {
287 auto group = groups[groupsIndex++];
288 group.setName(name);
289 
290 unsigned int structuresSize = sizeof...(Types);
291 auto structures = group.initStructures(structuresSize);
292 structureIndex = 0;
293 (writeStructure<Types>(builder, structures), ...);
294 KJ_ASSERT(structureIndex == structuresSize);
295 }
296 
297 kj::Maybe<kj::String> compatDate;
298 kj::Array<kj::String> compatFlags;
299 
300 unsigned int groupsIndex = 0;
301 unsigned int structureIndex = 0;
302};
303 
304} // namespace
305 
306kj::Array<byte> RTTIModule::exportTypes(kj::String compatDate, kj::Array<kj::String> compatFlags) {
307 TypesEncoder encoder(kj::mv(compatDate), kj::mv(compatFlags));
308 return encoder.encode();
309}
310 
311kj::Array<byte> RTTIModule::exportExperimentalTypes() {
312 TypesEncoder encoder;
313 return encoder.encode();
314}
315 
316} // namespace workerd::api