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