1 // Copyright 2014 Wouter van Oortmerssen. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef LOBSTER_NATREG
16 #define LOBSTER_NATREG
17
18 #include "lobster/vmdata.h"
19
20 namespace lobster {
21
22 // Compile time lifetime tracking between values and their recipients.
23 // If lifetimes correspond, no action is required.
24 // If recipient wants to keep, but value is borrowed, inc ref or copy or error.
25 // If recipient wants to borrow, but value is keep, dec ref or delete after recipient is done.
26 // NOTE: all positive values are an index of the SpecIdent being borrowed.
27 // If you're borrowing, you are "locking" the modification of the variable you borrow from.
28 enum Lifetime {
29 // Value: you are receiving a value stored elsewhere, do not hold on.
30 // Recipient: I do not want to be responsible for managing this value.
31 LT_BORROW = -1,
32 // Value: you are responsible for this value, you must delete or store.
33 // Recipient: I want to hold on to this value (inc ref, or be sole owner).
34 LT_KEEP = -2,
35 // Value: lifetime shouldn't matter, because type is non-reference.
36 // Recipient: I'm cool with any lifetime.
37 LT_ANY = -3,
38 // Value: there are multiple lifetimes, stored elsewhere.
39 LT_MULTIPLE = -4,
40 // Lifetime is not valid.
41 LT_UNDEF = -5,
42 };
43
IsBorrow(Lifetime lt)44 inline bool IsBorrow(Lifetime lt) { return lt >= LT_BORROW; }
LifetimeType(Lifetime lt)45 inline Lifetime LifetimeType(Lifetime lt) { return IsBorrow(lt) ? LT_BORROW : lt; }
46
47 struct Named {
48 string name;
49 int idx = -1;
50 bool isprivate = false;
51
52 Named() = default;
nameNamed53 Named(string_view _name, int _idx = 0) : name(_name), idx(_idx) {}
54 };
55
56 struct SubFunction;
57
58 struct Enum;
59
60 struct UDT;
61
62 struct Type {
63 const ValueType t = V_UNDEFINED;
64
65 struct TupleElem { const Type *type; Lifetime lt; };
66
67 union {
68 const Type *sub; // V_VECTOR | V_NIL | V_VAR
69 SubFunction *sf; // V_FUNCTION | V_COROUTINE
70 UDT *udt; // V_CLASS | V_STRUCT_*
71 Enum *e; // V_INT
72 vector<TupleElem> *tup; // V_TUPLE
73 };
74
TypeType75 Type() : sub(nullptr) {}
TypeType76 explicit Type(ValueType _t) : t(_t), sub(nullptr) {}
TypeType77 Type(ValueType _t, const Type *_s) : t(_t), sub(_s) {}
TypeType78 Type(ValueType _t, SubFunction *_sf) : t(_t), sf(_sf) {}
TypeType79 Type(ValueType _t, UDT *_udt) : t(_t), udt(_udt) {}
TypeType80 Type(Enum *_e) : t(V_INT), e(_e) {}
81
82 bool operator==(const Type &o) const {
83 return t == o.t &&
84 (sub == o.sub || // Also compares sf/udt
85 (Wrapped() && *sub == *o.sub));
86 }
87
88 bool operator!=(const Type &o) const { return !(*this == o); }
89
EqNoIndexType90 bool EqNoIndex(const Type &o) const {
91 return t == o.t && (!Wrapped() || sub->EqNoIndex(*o.sub));
92 }
93
94 Type &operator=(const Type &o) {
95 // Hack: we want t to be const, but still have a working assignment operator.
96 (ValueType &)t = o.t;
97 sub = o.sub;
98 return *this;
99 }
100
ElementType101 const Type *Element() const {
102 assert(Wrapped());
103 return sub;
104 }
105
ElementIfNilType106 const Type *ElementIfNil() const {
107 return t == V_NIL ? sub : this;
108 }
109
WrapType110 Type *Wrap(Type *dest, ValueType with) const {
111 *dest = Type(with, this);
112 return dest;
113 }
114
WrappedType115 bool Wrapped() const { return t == V_VECTOR || t == V_NIL; }
116
UnWrappedType117 const Type *UnWrapped() const { return Wrapped() ? sub : this; }
118
NumericType119 bool Numeric() const { return t == V_INT || t == V_FLOAT; }
120
IsFunctionType121 bool IsFunction() const { return t == V_FUNCTION && sf; }
122
IsEnumType123 bool IsEnum() const { return t == V_INT && e; }
124
IsBoundVarType125 bool IsBoundVar() const { return t == V_VAR && sub; }
126
HasValueTypeType127 bool HasValueType(ValueType vt) const {
128 return t == vt || (Wrapped() && Element()->HasValueType(vt));
129 }
130
NumValuesType131 size_t NumValues() const {
132 if (t == V_VOID) return 0;
133 if (t == V_TUPLE) return tup->size();
134 return 1;
135 }
136
GetType137 const Type *Get(size_t i) const {
138 return t == V_TUPLE ? (*tup)[i].type : this;
139 }
140
SetType141 void Set(size_t i, const Type *type, Lifetime lt) const {
142 assert(t == V_TUPLE);
143 (*tup)[i] = { type, lt };
144 }
145
GetLifetimeType146 Lifetime GetLifetime(size_t i, Lifetime lt) const {
147 return lt == LT_MULTIPLE && t == V_TUPLE ? (*tup)[i].lt : lt;
148 }
149 };
150
151 extern const Type g_type_undefined;
152
153 // This is essentially a smart-pointer, but behaves a little bit differently:
154 // - initialized to type_undefined instead of nullptr
155 // - pointer is const
156 // - comparisons are by value.
157 class TypeRef {
158 const Type *type;
159
160 public:
TypeRef()161 TypeRef() : type(&g_type_undefined) {}
TypeRef(const Type * _type)162 TypeRef(const Type *_type) : type(_type) {}
163
164 TypeRef &operator=(const TypeRef &o) {
165 type = o.type;
166 return *this;
167 }
168
169 const Type &operator*() const { return *type; }
170 const Type *operator->() const { return type; }
171
get()172 const Type *get() const { return type; }
173
174 // Must compare Type instances by value.
175 bool operator==(const TypeRef &o) const { return *type == *o.type; };
176 bool operator!=(const TypeRef &o) const { return *type != *o.type; };
177
Null()178 bool Null() const { return type == nullptr; }
179 };
180
181 extern TypeRef type_int;
182 extern TypeRef type_float;
183 extern TypeRef type_string;
184 extern TypeRef type_any;
185 extern TypeRef type_vector_int;
186 extern TypeRef type_vector_float;
187 extern TypeRef type_function_null;
188 extern TypeRef type_function_cocl;
189 extern TypeRef type_function_void;
190 extern TypeRef type_coroutine;
191 extern TypeRef type_resource;
192 extern TypeRef type_typeid;
193 extern TypeRef type_void;
194 extern TypeRef type_undefined;
195
196 TypeRef WrapKnown(TypeRef elem, ValueType with);
197
198 enum ArgFlags {
199 AF_NONE = 0,
200 AF_EXPFUNVAL = 1,
201 AF_GENERIC = 2,
202 NF_SUBARG1 = 4,
203 NF_SUBARG2 = 8,
204 NF_SUBARG3 = 16,
205 NF_ANYVAR = 32,
206 NF_CORESUME = 64,
207 AF_WITHTYPE = 128,
208 NF_CONVERTANYTOSTRING = 256,
209 NF_PUSHVALUEWIDTH = 512,
210 NF_BOOL = 1024,
211 };
212 DEFINE_BITWISE_OPERATORS_FOR_ENUM(ArgFlags)
213
214 struct Ident;
215 struct SpecIdent;
216
217 struct Typed {
218 TypeRef type = type_undefined;
219 ArgFlags flags = AF_NONE;
220
221 Typed() = default;
TypedTyped222 Typed(const Typed &o) : type(o.type), flags(o.flags) {}
TypedTyped223 Typed(TypeRef _type, ArgFlags _flags) : type(_type), flags(_flags) {}
224 };
225
226 struct Narg : Typed {
227 char fixed_len = 0;
228 Lifetime lt = LT_UNDEF;
229
230 Narg() = default;
NargNarg231 Narg(const Narg &o) : Typed(o), fixed_len(o.fixed_len), lt(o.lt) {}
232
SetNarg233 void Set(const char *&tid, Lifetime def) {
234 char t = *tid++;
235 flags = AF_NONE;
236 lt = def;
237 switch (t) {
238 case 'A': type = type_any; break;
239 case 'I': type = type_int; break;
240 case 'B': type = type_int; flags = flags | NF_BOOL; break;
241 case 'F': type = type_float; break;
242 case 'S': type = type_string; break;
243 case 'L': type = type_function_null; break;
244 case 'C': type = type_coroutine; break;
245 case 'R': type = type_resource; break;
246 case 'T': type = type_typeid; break;
247 default: assert(0);
248 }
249 while (*tid && !isupper(*tid)) {
250 switch (auto c = *tid++) {
251 case 0: break;
252 case '1': flags = flags | NF_SUBARG1; break;
253 case '2': flags = flags | NF_SUBARG2; break;
254 case '3': flags = flags | NF_SUBARG3; break;
255 case '*': flags = flags | NF_ANYVAR; break;
256 case '@': flags = flags | AF_EXPFUNVAL; break;
257 case '%': flags = flags | NF_CORESUME; break; // FIXME: make a vm op.
258 case 's': flags = flags | NF_CONVERTANYTOSTRING; break;
259 case 'w': flags = flags | NF_PUSHVALUEWIDTH; break;
260 case 'k': lt = LT_KEEP; break;
261 case 'b': lt = LT_BORROW; break;
262 case ']':
263 case '}':
264 type = WrapKnown(type, V_VECTOR);
265 assert(!type.Null());
266 if (c == '}') fixed_len = -1;
267 break;
268 case '?':
269 type = WrapKnown(type, V_NIL);
270 assert(!type.Null());
271 break;
272 case ':':
273 assert(*tid >= '/' && *tid <= '9');
274 fixed_len = *tid++ - '0';
275 break;
276 default:
277 assert(false);
278 }
279 }
280 }
281 };
282
283 struct GenericArgs {
284 virtual string_view GetName(size_t i) const = 0;
285 virtual TypeRef GetType(size_t i) const = 0;
286 virtual ArgFlags GetFlags(size_t i) const = 0;
287 virtual size_t size() const = 0;
288 };
289
290 struct NargVector : GenericArgs {
291 vector<Narg> v;
292 const char *idlist;
293
NargVectorNargVector294 NargVector(size_t nargs, const char *_idlist) : v(nargs), idlist(_idlist) {}
295
sizeNargVector296 size_t size() const { return v.size(); }
GetTypeNargVector297 TypeRef GetType(size_t i) const { return v[i].type; }
GetFlagsNargVector298 ArgFlags GetFlags(size_t i) const { return v[i].flags; }
GetNameNargVector299 string_view GetName(size_t i) const {
300 auto ids = idlist;
301 for (;;) {
302 const char *idend = strchr(ids, ',');
303 if (!idend) {
304 // if this fails, you're not specifying enough arg names in the comma separated list
305 assert(!i);
306 idend = ids + strlen(ids);
307 }
308 if (!i--) return string_view(ids, idend - ids);
309 ids = idend + 1;
310 }
311 }
312 };
313
314 typedef void (*builtinfV)(VM &vm);
315 typedef Value (*builtinf0)(VM &vm);
316 typedef Value (*builtinf1)(VM &vm, Value &);
317 typedef Value (*builtinf2)(VM &vm, Value &, Value &);
318 typedef Value (*builtinf3)(VM &vm, Value &, Value &, Value &);
319 typedef Value (*builtinf4)(VM &vm, Value &, Value &, Value &, Value &);
320 typedef Value (*builtinf5)(VM &vm, Value &, Value &, Value &, Value &, Value &);
321 typedef Value (*builtinf6)(VM &vm, Value &, Value &, Value &, Value &, Value &, Value &);
322 typedef Value (*builtinf7)(VM &vm, Value &, Value &, Value &, Value &, Value &, Value &, Value &);
323
324 struct BuiltinPtr {
325 union {
326 builtinfV fV;
327 builtinf0 f0;
328 builtinf1 f1;
329 builtinf2 f2;
330 builtinf3 f3;
331 builtinf4 f4;
332 builtinf5 f5;
333 builtinf6 f6;
334 builtinf7 f7;
335 };
336 int fnargs;
337
BuiltinPtrBuiltinPtr338 BuiltinPtr() : f0(nullptr), fnargs(0) {}
BuiltinPtrBuiltinPtr339 BuiltinPtr(builtinfV f) : fV(f), fnargs(-1) {}
BuiltinPtrBuiltinPtr340 BuiltinPtr(builtinf0 f) : f0(f), fnargs(0) {}
BuiltinPtrBuiltinPtr341 BuiltinPtr(builtinf1 f) : f1(f), fnargs(1) {}
BuiltinPtrBuiltinPtr342 BuiltinPtr(builtinf2 f) : f2(f), fnargs(2) {}
BuiltinPtrBuiltinPtr343 BuiltinPtr(builtinf3 f) : f3(f), fnargs(3) {}
BuiltinPtrBuiltinPtr344 BuiltinPtr(builtinf4 f) : f4(f), fnargs(4) {}
BuiltinPtrBuiltinPtr345 BuiltinPtr(builtinf5 f) : f5(f), fnargs(5) {}
BuiltinPtrBuiltinPtr346 BuiltinPtr(builtinf6 f) : f6(f), fnargs(6) {}
BuiltinPtrBuiltinPtr347 BuiltinPtr(builtinf7 f) : f7(f), fnargs(7) {}
348 };
349
350 struct NativeFun : Named {
351 BuiltinPtr fun;
352
353 NargVector args, retvals;
354
355 builtinfV cont1;
356
357 const char *idlist;
358 const char *help;
359
360 int subsystemid = -1;
361
362 NativeFun *overloads = nullptr, *first = this;
363
TypeLenNativeFun364 int TypeLen(const char *s) {
365 int i = 0;
366 while (*s) if(isupper(*s++)) i++;
367 return i;
368 };
369
NativeFunNativeFun370 NativeFun(const char *name, BuiltinPtr f, const char *ids, const char *typeids,
371 const char *rets, const char *help, builtinfV cont1)
372 : Named(name, 0), fun(f), args(TypeLen(typeids), ids), retvals(0, nullptr),
373 cont1(cont1), help(help) {
374 auto nretvalues = TypeLen(rets);
375 assert((int)args.v.size() == f.fnargs || f.fnargs < 0);
376 auto StructArgsVararg = [&](const Narg &arg) {
377 assert(!arg.fixed_len || IsRef(arg.type->sub->t) || f.fnargs < 0);
378 (void)arg;
379 };
380 for (size_t i = 0; i < args.v.size(); i++) {
381 args.GetName(i); // Call this just to trigger the assert.
382 args.v[i].Set(typeids, LT_BORROW);
383 StructArgsVararg(args.v[i]);
384 }
385 for (int i = 0; i < nretvalues; i++) {
386 retvals.v.push_back(Narg());
387 retvals.v[i].Set(rets, LT_KEEP);
388 StructArgsVararg(retvals.v[i]);
389 }
390 }
391
CanChangeControlFlowNativeFun392 bool CanChangeControlFlow() {
393 // FIXME: make resume a VM op.
394 return name == "resume" || name == "gl_frame";
395 }
396
IsAssertNativeFun397 bool IsAssert() {
398 // FIXME: make into a language feature.
399 return name == "assert";
400 }
401 };
402
403 struct NativeRegistry {
404 vector<NativeFun *> nfuns;
405 unordered_map<string_view, NativeFun *> nfunlookup; // Key points to value!
406 vector<string> subsystems;
407
~NativeRegistryNativeRegistry408 ~NativeRegistry() {
409 for (auto f : nfuns) delete f;
410 }
411
NativeSubSystemStartNativeRegistry412 void NativeSubSystemStart(const char *name) { subsystems.push_back(name); }
413
414 #define REGISTER(N) \
415 void operator()(const char *name, const char *ids, const char *typeids, \
416 const char *rets, const char *help, builtinf##N f, \
417 builtinfV cont1 = nullptr) { \
418 Reg(new NativeFun(name, BuiltinPtr(f), ids, typeids, rets, help, cont1)); \
419 }
420 REGISTER(V)
421 REGISTER(0)
422 REGISTER(1)
423 REGISTER(2)
424 REGISTER(3)
425 REGISTER(4)
426 REGISTER(5)
427 REGISTER(6)
428 REGISTER(7)
429 #undef REGISTER
430
RegNativeRegistry431 void Reg(NativeFun *nf) {
432 nf->idx = (int)nfuns.size();
433 nf->subsystemid = (int)subsystems.size() - 1;
434 auto existing = FindNative(nf->name);
435 if (existing) {
436 if (/*nf->args.v.size() != existing->args.v.size() ||
437 nf->retvals.v.size() != existing->retvals.v.size() || */
438 nf->subsystemid != existing->subsystemid ) {
439 // Must have similar signatures.
440 assert(0);
441 THROW_OR_ABORT("native library name clash: " + nf->name);
442 }
443 nf->overloads = existing->overloads;
444 existing->overloads = nf;
445 nf->first = existing->first;
446 } else {
447 nfunlookup[nf->name /* must be in value */] = nf;
448 }
449 nfuns.push_back(nf);
450 }
451
FindNativeNativeRegistry452 NativeFun *FindNative(string_view name) {
453 auto it = nfunlookup.find(name);
454 return it != nfunlookup.end() ? it->second : nullptr;
455 }
456 };
457
458 } // namespace lobster
459
460 #endif // LOBSTER_NATREG
461