/src/moddable/xs/sources/xsPlatforms.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2016-2025 Moddable Tech, Inc. |
3 | | * |
4 | | * This file is part of the Moddable SDK Runtime. |
5 | | * |
6 | | * The Moddable SDK Runtime is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation, either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * The Moddable SDK Runtime is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public License |
17 | | * along with the Moddable SDK Runtime. If not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | * This file incorporates work covered by the following copyright and |
20 | | * permission notice: |
21 | | * |
22 | | * Copyright (C) 2010-2016 Marvell International Ltd. |
23 | | * Copyright (C) 2002-2010 Kinoma, Inc. |
24 | | * |
25 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
26 | | * you may not use this file except in compliance with the License. |
27 | | * You may obtain a copy of the License at |
28 | | * |
29 | | * http://www.apache.org/licenses/LICENSE-2.0 |
30 | | * |
31 | | * Unless required by applicable law or agreed to in writing, software |
32 | | * distributed under the License is distributed on an "AS IS" BASIS, |
33 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
34 | | * See the License for the specific language governing permissions and |
35 | | * limitations under the License. |
36 | | */ |
37 | | |
38 | | #include "xsAll.h" |
39 | | #include "xsScript.h" |
40 | | |
41 | | /* old programming interface defaults to 0 */ |
42 | | |
43 | | #ifndef mxUseDefaultMachinePlatform |
44 | | #define mxUseDefaultMachinePlatform 0 |
45 | | #endif |
46 | | #ifndef mxUseDefaultBuildKeys |
47 | | #define mxUseDefaultBuildKeys 0 |
48 | | #endif |
49 | | #ifndef mxUseDefaultChunkAllocation |
50 | | #define mxUseDefaultChunkAllocation 0 |
51 | | #endif |
52 | | #ifndef mxUseDefaultSlotAllocation |
53 | | #define mxUseDefaultSlotAllocation 0 |
54 | | #endif |
55 | | #ifndef mxUseDefaultFindModule |
56 | | #define mxUseDefaultFindModule 0 |
57 | | #endif |
58 | | #ifndef mxUseDefaultLoadModule |
59 | | #define mxUseDefaultLoadModule 0 |
60 | | #endif |
61 | | #ifndef mxUseDefaultParseScript |
62 | | #define mxUseDefaultParseScript 0 |
63 | | #endif |
64 | | #ifndef mxUseDefaultQueuePromiseJobs |
65 | | #define mxUseDefaultQueuePromiseJobs 0 |
66 | | #endif |
67 | | #ifndef mxUseDefaultAbort |
68 | | #define mxUseDefaultAbort 0 |
69 | | #endif |
70 | | #ifndef mxUseDefaultDebug |
71 | | #define mxUseDefaultDebug 0 |
72 | | #endif |
73 | | |
74 | | #if mxUseDefaultMachinePlatform |
75 | | |
76 | | void fxCreateMachinePlatform(txMachine* the) |
77 | | { |
78 | | } |
79 | | |
80 | | void fxDeleteMachinePlatform(txMachine* the) |
81 | | { |
82 | | } |
83 | | |
84 | | #endif /* mxUseDefaultMachinePlatform */ |
85 | | |
86 | | #if mxUseDefaultBuildKeys |
87 | | |
88 | | void fxBuildKeys(txMachine* the) |
89 | 21.8k | { |
90 | 21.8k | int i = 0; |
91 | 21.8k | { |
92 | 21.8k | txSlot* key = fxFindKey(the); |
93 | 21.8k | key->flag = XS_INTERNAL_FLAG | XS_DONT_DELETE_FLAG; |
94 | 21.8k | i++; |
95 | 21.8k | } |
96 | 348k | for (; i < XS_SYMBOL_ID_COUNT; i++) { |
97 | 327k | txSlot* key = fxFindKey(the); |
98 | 327k | txSlot* instance = fxNewInstance(the); |
99 | 327k | txSlot* property = fxNextSymbolProperty(the, instance, i, XS_NO_ID, XS_INTERNAL_FLAG); |
100 | 327k | fxNextStringXProperty(the, property, gxIDStrings[i], XS_NO_ID, XS_INTERNAL_FLAG); |
101 | 327k | key->flag = XS_INTERNAL_FLAG | XS_DONT_DELETE_FLAG; |
102 | 327k | key->kind = XS_REFERENCE_KIND; |
103 | 327k | key->value.reference = instance; |
104 | 327k | mxPop(); |
105 | 327k | } |
106 | 11.1M | for (; i < XS_ID_COUNT; i++) { |
107 | 11.1M | fxID(the, gxIDStrings[i]); |
108 | 11.1M | } |
109 | 21.8k | } |
110 | | |
111 | | #endif /* mxUseDefaultBuildKeys */ |
112 | | |
113 | | |
114 | | #if mxUseDefaultChunkAllocation |
115 | | |
116 | | void* fxAllocateChunks(txMachine* the, txSize theSize) |
117 | 24.6k | { |
118 | 24.6k | return c_malloc(theSize); |
119 | 24.6k | } |
120 | | |
121 | | void fxFreeChunks(txMachine* the, void* theChunks) |
122 | 24.6k | { |
123 | 24.6k | c_free(theChunks); |
124 | 24.6k | } |
125 | | |
126 | | #endif /* mxUseDefaultChunkAllocation */ |
127 | | |
128 | | |
129 | | #if mxUseDefaultSlotAllocation |
130 | | |
131 | | txSlot* fxAllocateSlots(txMachine* the, txSize theCount) |
132 | 45.4k | { |
133 | 45.4k | return(txSlot*)c_malloc(theCount * sizeof(txSlot)); |
134 | 45.4k | } |
135 | | |
136 | | void fxFreeSlots(txMachine* the, void* theSlots) |
137 | 45.4k | { |
138 | 45.4k | c_free(theSlots); |
139 | 45.4k | } |
140 | | |
141 | | #endif /* mxUseDefaultSlotAllocation */ |
142 | | |
143 | | #if mxUseDefaultFindModule |
144 | | |
145 | | txID fxFindModule(txMachine* the, txSlot* realm, txID moduleID, txSlot* slot) |
146 | | { |
147 | | txPreparation* preparation = the->preparation; |
148 | | char name[C_PATH_MAX]; |
149 | | #if MODDEF_XS_TEST |
150 | | char extension[5] = ""; |
151 | | #endif |
152 | | char buffer[C_PATH_MAX]; |
153 | | txInteger dot = 0; |
154 | | txInteger i = 0; |
155 | | txInteger hash = 0; |
156 | | txString slash; |
157 | | txString path; |
158 | | fxToStringBuffer(the, slot, name, sizeof(name)); |
159 | | if (name[0] == '.') { |
160 | | if (name[1] == '/') { |
161 | | dot = 1; |
162 | | i = 1; |
163 | | } |
164 | | else if ((name[1] == '.') && (name[2] == '/')) { |
165 | | dot = 2; |
166 | | i = 2; |
167 | | while ((name[i + 1] == '.') && (name[i + 2] == '.') && (name[i + 3] == '/')) { |
168 | | dot++; |
169 | | i += 3; |
170 | | } |
171 | | } |
172 | | } |
173 | | else if (name[0] == '#') { |
174 | | hash = 1; |
175 | | } |
176 | | else if (c_strncmp(name, "moddable:", 9) == 0) |
177 | | c_memmove(name, name + 9, c_strlen(name) - 8); |
178 | | |
179 | | #if mxWindows |
180 | | { |
181 | | char c; |
182 | | slash = name; |
183 | | if (!c_strncmp(name, "xsbug://", 8)) |
184 | | slash += 8; |
185 | | while ((c = *slash)) { |
186 | | if (c == '/') |
187 | | *slash = '\\'; |
188 | | slash++; |
189 | | } |
190 | | } |
191 | | #endif |
192 | | slash = c_strrchr(name, mxSeparator); |
193 | | if (!slash) |
194 | | slash = name; |
195 | | slash = c_strrchr(slash, '.'); |
196 | | if (slash && (!c_strcmp(slash, ".js") || !c_strcmp(slash, ".mjs"))) { |
197 | | #if MODDEF_XS_TEST |
198 | | c_strcpy(extension, slash); |
199 | | #endif |
200 | | *slash = 0; |
201 | | } |
202 | | if (dot > 0) { |
203 | | if (moduleID == XS_NO_ID) |
204 | | return XS_NO_ID; |
205 | | buffer[0] = mxSeparator; |
206 | | path = buffer + 1; |
207 | | c_strcpy(path, fxGetKeyName(the, moduleID)); |
208 | | slash = c_strrchr(buffer, mxSeparator); |
209 | | if (!slash) |
210 | | return XS_NO_ID; |
211 | | *slash = 0; |
212 | | dot--; |
213 | | while (dot > 0) { |
214 | | slash = c_strrchr(buffer, mxSeparator); |
215 | | if (!slash) |
216 | | return XS_NO_ID; |
217 | | *slash = 0; |
218 | | dot--; |
219 | | } |
220 | | if ((c_strlen(buffer) + c_strlen(name + i)) >= sizeof(buffer)) |
221 | | mxRangeError("path too long"); |
222 | | c_strcat(buffer, name + i); |
223 | | } |
224 | | else if (hash) { |
225 | | if (moduleID == XS_NO_ID) |
226 | | return XS_NO_ID; |
227 | | path = buffer; |
228 | | c_strcpy(path, fxGetKeyName(the, moduleID)); |
229 | | slash = c_strchr(buffer, mxSeparator); |
230 | | if (!slash) |
231 | | return XS_NO_ID; |
232 | | if (path[0] == '@') { |
233 | | slash = c_strchr(slash + 1, mxSeparator); |
234 | | if (!slash) |
235 | | return XS_NO_ID; |
236 | | } |
237 | | *(slash + 1) = 0; |
238 | | if ((c_strlen(buffer) + c_strlen(name)) >= sizeof(buffer)) |
239 | | mxRangeError("path too long"); |
240 | | c_strcat(buffer, name); |
241 | | } |
242 | | else |
243 | | path = name; |
244 | | if (preparation) { |
245 | | txInteger c = preparation->scriptCount; |
246 | | txScript* script = preparation->scripts; |
247 | | size_t size; |
248 | | if (fxGetArchiveCode(the, the->archive, path, &size)) |
249 | | return fxNewNameC(the, path); |
250 | | while (c > 0) { |
251 | | if (!c_strcmp(path, script->path)) |
252 | | return fxNewNameC(the, path); |
253 | | c--; |
254 | | script++; |
255 | | } |
256 | | } |
257 | | #if MODDEF_XS_TEST |
258 | | if (!c_strncmp(path, "xsbug://", 8)) { |
259 | | c_strcat(path, extension); |
260 | | return fxNewNameC(the, path); |
261 | | } |
262 | | #endif |
263 | | return XS_NO_ID; |
264 | | } |
265 | | |
266 | | #endif /* mxUseDefaultFindModule */ |
267 | | |
268 | | #if mxUseDefaultLoadModule |
269 | | |
270 | | #if MODDEF_XS_TEST |
271 | | extern void fxDebugImport(txMachine* the, txSlot* module, txString path); |
272 | | #endif |
273 | | |
274 | | void fxLoadModule(txMachine* the, txSlot* module, txID moduleID) |
275 | | { |
276 | | txString path = fxGetKeyName(the, moduleID); |
277 | | txByte* code; |
278 | | size_t size; |
279 | | code = fxGetArchiveCode(the, the->archive, path, &size); |
280 | | if (code) { |
281 | | txScript script; |
282 | | script.callback = NULL; |
283 | | script.symbolsBuffer = NULL; |
284 | | script.symbolsSize = 0; |
285 | | script.codeBuffer = code; |
286 | | script.codeSize = (txSize)size; |
287 | | script.hostsBuffer = NULL; |
288 | | script.hostsSize = 0; |
289 | | script.path = path; |
290 | | script.version[0] = XS_MAJOR_VERSION; |
291 | | script.version[1] = XS_MINOR_VERSION; |
292 | | script.version[2] = XS_PATCH_VERSION; |
293 | | script.version[3] = 0; |
294 | | fxResolveModule(the, module, moduleID, &script, C_NULL, C_NULL); |
295 | | } |
296 | | else { |
297 | | txPreparation* preparation = the->preparation; |
298 | | if (preparation) { |
299 | | txInteger c = preparation->scriptCount; |
300 | | txScript* script = preparation->scripts; |
301 | | while (c > 0) { |
302 | | if (!c_strcmp(path, script->path)) { |
303 | | fxResolveModule(the, module, moduleID, script, C_NULL, C_NULL); |
304 | | return; |
305 | | } |
306 | | c--; |
307 | | script++; |
308 | | } |
309 | | } |
310 | | } |
311 | | #if MODDEF_XS_TEST |
312 | | if (!c_strncmp(path, "xsbug://", 8)) { |
313 | | fxDebugImport(the, module, path); |
314 | | return; |
315 | | } |
316 | | #endif |
317 | | } |
318 | | |
319 | | #endif /* mxUseDefaultLoadModule */ |
320 | | |
321 | | #if mxUseDefaultParseScript |
322 | | |
323 | | txScript* fxParseScript(txMachine* the, void* stream, txGetter getter, txUnsigned flags) |
324 | 405k | { |
325 | 405k | txParser _parser; |
326 | 405k | txParser* parser = &_parser; |
327 | 405k | txParserJump jump; |
328 | 405k | txScript* script = NULL; |
329 | 405k | fxInitializeParser(parser, the, the->parserBufferSize, the->parserTableModulo); |
330 | 405k | parser->firstJump = &jump; |
331 | 405k | if (c_setjmp(jump.jmp_buf) == 0) { |
332 | 405k | fxParserTree(parser, stream, getter, flags, NULL); |
333 | 405k | #ifdef mxDebug |
334 | 405k | parser->flags |= mxDebugFlag; |
335 | 405k | if (!the->debugEval) { |
336 | 405k | if (!parser->source) { |
337 | 405k | char tag[16]; |
338 | 405k | parser->flags |= mxDebugFlag; |
339 | 405k | fxGenerateTag(the, tag, sizeof(tag), C_NULL); |
340 | 405k | parser->source = fxNewParserSymbol(parser, tag); |
341 | 405k | } |
342 | 405k | if (fxIsConnected(the)) { |
343 | 0 | if (getter == fxStringGetter) |
344 | 0 | fxFileEvalString(the, ((txStringStream*)stream)->slot->value.string, parser->source->string); |
345 | 0 | else if (getter == fxStringCGetter) |
346 | 0 | fxFileEvalString(the, ((txStringCStream*)stream)->buffer, parser->source->string); |
347 | 0 | } |
348 | 405k | } |
349 | 405k | #endif |
350 | 405k | fxParserHoist(parser); |
351 | 405k | fxParserBind(parser); |
352 | 405k | script = fxParserCode(parser); |
353 | 405k | } |
354 | | #ifdef mxInstrument |
355 | | if (the->peakParserSize < parser->total) |
356 | | the->peakParserSize = parser->total; |
357 | | #endif |
358 | 405k | fxTerminateParser(parser); |
359 | 405k | return script; |
360 | 405k | } |
361 | | |
362 | | #endif /* mxUseDefaultParseScript */ |
363 | | |
364 | | #if mxUseDefaultQueuePromiseJobs |
365 | | |
366 | | void fxQueuePromiseJobs(txMachine* the) |
367 | | { |
368 | | mxUnknownError("promise: no queue"); |
369 | | } |
370 | | |
371 | | #endif /* mxUseDefaultQueuePromiseJobs */ |
372 | | |
373 | | #if mxUseDefaultAbort |
374 | | |
375 | | void fxAbort(txMachine* the, int status) |
376 | | { |
377 | | txString fxAbortString(int status); |
378 | | txString why = fxAbortString(status); |
379 | | #ifdef mxDebug |
380 | | if (status == XS_DEAD_STRIP_EXIT) { |
381 | | if (the->debugEval) |
382 | | mxUnknownError(why); |
383 | | } |
384 | | #endif |
385 | | fprintf(stderr, "Error: %s\n", why); |
386 | | c_exit(status); |
387 | | } |
388 | | |
389 | | #endif /* mxUseDefaultAbort */ |
390 | | |
391 | | #ifdef mxDebug |
392 | | |
393 | | #if mxUseDefaultDebug |
394 | | |
395 | | void fxConnect(txMachine* the) |
396 | | { |
397 | | } |
398 | | |
399 | | void fxDisconnect(txMachine* the) |
400 | | { |
401 | | } |
402 | | |
403 | | txBoolean fxIsConnected(txMachine* the) |
404 | | { |
405 | | return 0; |
406 | | } |
407 | | |
408 | | txBoolean fxIsReadable(txMachine* the) |
409 | | { |
410 | | return 0; |
411 | | } |
412 | | |
413 | | void fxReceive(txMachine* the) |
414 | | { |
415 | | } |
416 | | |
417 | | void fxSend(txMachine* the, txBoolean more) |
418 | | { |
419 | | } |
420 | | |
421 | | #endif /* mxUseDefaultDebug */ |
422 | | |
423 | | #endif |
424 | | |
425 | | #if mxWindows || mxMacOSX || mxLinux || mxWasm |
426 | | uint32_t modMilliseconds() |
427 | 0 | { |
428 | 0 | c_timeval tv; |
429 | 0 | c_gettimeofday(&tv, NULL); |
430 | | // #if (mxWasm || mxWindows || mxMacOSX) |
431 | 0 | return (uint32_t)(uint64_t)(((double)(tv.tv_sec) * 1000.0) + ((double)(tv.tv_usec) / 1000.0)); |
432 | | // #else |
433 | | // return (uint32_t)(((double)(tv.tv_sec) * 1000.0) + ((double)(tv.tv_usec) / 1000.0)); |
434 | | // #endif |
435 | 0 | } |
436 | | #endif |
437 | | |
438 | | #if mxWindows |
439 | | |
440 | | #if _MSC_VER < 1800 |
441 | | |
442 | | unsigned long c_nan[2]={0xffffffff, 0x7fffffff}; |
443 | | unsigned long c_infinity[2]={0x00000000, 0x7ff00000}; |
444 | | |
445 | | int c_fpclassify(double x) |
446 | | { |
447 | | int result = FP_NORMAL; |
448 | | switch (_fpclass(x)) { |
449 | | case _FPCLASS_SNAN: |
450 | | case _FPCLASS_QNAN: |
451 | | result = FP_NAN; |
452 | | break; |
453 | | case _FPCLASS_NINF: |
454 | | case _FPCLASS_PINF: |
455 | | result = FP_INFINITE; |
456 | | break; |
457 | | case _FPCLASS_NZ: |
458 | | case _FPCLASS_PZ: |
459 | | result = FP_ZERO; |
460 | | break; |
461 | | case _FPCLASS_ND: |
462 | | case _FPCLASS_PD: |
463 | | result = FP_SUBNORMAL; |
464 | | break; |
465 | | } |
466 | | return result; |
467 | | } |
468 | | |
469 | | #endif /* _MSC_VER < 1800 */ |
470 | | |
471 | | int c_gettimeofday(c_timeval *tp, struct c_timezone *tzp) |
472 | | { |
473 | | struct _timeb tb; |
474 | | |
475 | | _ftime(&tb); |
476 | | if (tp != 0) { |
477 | | tp->tv_sec = (long)tb.time; |
478 | | tp->tv_usec = tb.millitm * 1000; |
479 | | } |
480 | | if (tzp != 0) { |
481 | | tzp->tz_minuteswest = tb.timezone; |
482 | | tzp->tz_dsttime = tb.dstflag; |
483 | | } |
484 | | return (0); |
485 | | } |
486 | | |
487 | | char *c_realpath(const char *path, char *real) |
488 | | { |
489 | | if (_fullpath(real, path, C_PATH_MAX) != NULL) { |
490 | | DWORD attributes = GetFileAttributes(real); |
491 | | if (attributes != 0xFFFFFFFF) { |
492 | | return real; |
493 | | } |
494 | | } |
495 | | return C_NULL; |
496 | | } |
497 | | |
498 | | #endif |