Current section
26 Versions
Jump to
Current section
26 Versions
Compare versions
16
files changed
+985
additions
-166
deletions
| @@ -0,0 +1,13 @@ | |
| 1 | + "use strict"; |
| 2 | + |
| 3 | + import ERTS from "../erts.mjs"; |
| 4 | + |
| 5 | + const Elixir_Application = { |
| 6 | + // TODO: provide a more complete implementation. |
| 7 | + // Simplified temporary implementation - reads from client-side application env storage. |
| 8 | + "get_env/3": (app, key, defaultValue) => { |
| 9 | + return ERTS.applicationEnv.get(app, key, defaultValue); |
| 10 | + }, |
| 11 | + }; |
| 12 | + |
| 13 | + export default Elixir_Application; |
| @@ -1,5 +1,7 @@ | |
| 1 1 | "use strict"; |
| 2 2 | |
| 3 | + import Bitstring from "../bitstring.mjs"; |
| 4 | + import Erlang from "../erlang/erlang.mjs"; |
| 3 5 | import HologramInterpreterError from "../errors/interpreter_error.mjs"; |
| 4 6 | import Interpreter from "../interpreter.mjs"; |
| 5 7 | import Type from "../type.mjs"; |
| @@ -39,6 +41,32 @@ const Elixir_IO = { | |
| 39 41 | |
| 40 42 | return term; |
| 41 43 | }, |
| 44 | + |
| 45 | + // Deps: [IO.warn/2] |
| 46 | + "warn/1": (message) => { |
| 47 | + return Elixir_IO["warn/2"](message, Type.list()); |
| 48 | + }, |
| 49 | + |
| 50 | + // TODO: provide a more complete implementation. |
| 51 | + // Simplified temporary implementation - just prints the message to the console. |
| 52 | + // The second argument (stacktrace options) is ignored on the client side. |
| 53 | + // Deps: [:erlang.iolist_to_binary/1] |
| 54 | + "warn/2": (message, _stacktraceOrOpts) => { |
| 55 | + const binary = Erlang["iolist_to_binary/1"](message); |
| 56 | + console.warn(Bitstring.toText(binary)); |
| 57 | + |
| 58 | + return Type.atom("ok"); |
| 59 | + }, |
| 60 | + |
| 61 | + // TODO: provide a more complete implementation. |
| 62 | + // Simplified temporary implementation - evaluates the message function and delegates to warn/2. |
| 63 | + // Deps: [IO.warn/2] |
| 64 | + "warn_once/3": (_key, messageFun, _stacktraceDropLevels) => { |
| 65 | + return Elixir_IO["warn/2"]( |
| 66 | + Interpreter.callAnonymousFunction(messageFun, []), |
| 67 | + Type.list(), |
| 68 | + ); |
| 69 | + }, |
| 42 70 | }; |
| 43 71 | |
| 44 72 | export default Elixir_IO; |
| @@ -1373,38 +1373,30 @@ const Erlang = { | |
| 1373 1373 | // End integer_to_list/2 |
| 1374 1374 | // Deps: [] |
| 1375 1375 | |
| 1376 | - // TODO: test |
| 1377 1376 | // Start iolist_to_binary/1 |
| 1378 1377 | "iolist_to_binary/1": (ioListOrBinary) => { |
| 1379 | - // TODO: validate arg |
| 1380 | - |
| 1381 | - if (Type.isBitstring(ioListOrBinary)) { |
| 1378 | + if (Type.isBinary(ioListOrBinary)) { |
| 1382 1379 | return ioListOrBinary; |
| 1383 1380 | } |
| 1384 1381 | |
| 1385 | - const chunks = Erlang_Lists["flatten/1"](ioListOrBinary).data.map( |
| 1386 | - (term) => { |
| 1387 | - // TODO: validate list item (binary or integer allowed) |
| 1382 | + if (!Type.isList(ioListOrBinary)) { |
| 1383 | + Interpreter.raiseArgumentError( |
| 1384 | + Interpreter.buildArgumentErrorMsg(1, "not an iodata term"), |
| 1385 | + ); |
| 1386 | + } |
| 1388 1387 | |
| 1389 | - if (Type.isBitstring(term)) { |
| 1390 | - return term; |
| 1391 | - } |
| 1392 | - |
| 1393 | - const segment = Type.bitstringSegment(term, { |
| 1394 | - type: "integer", |
| 1395 | - size: Type.integer(8), |
| 1396 | - unit: 1n, |
| 1397 | - endianness: "big", |
| 1398 | - }); |
| 1399 | - |
| 1400 | - return Bitstring.fromSegmentWithIntegerValue(segment); |
| 1401 | - }, |
| 1402 | - ); |
| 1403 | - |
| 1404 | - return Bitstring.concat(chunks); |
| 1388 | + // :erlang.list_to_binary/1 raises ArgumentError "not an iolist term" on invalid input. |
| 1389 | + // Remap to "not an iodata term" to match OTP's iolist_to_binary/1 behavior. |
| 1390 | + try { |
| 1391 | + return Erlang["list_to_binary/1"](ioListOrBinary); |
| 1392 | + } catch { |
| 1393 | + Interpreter.raiseArgumentError( |
| 1394 | + Interpreter.buildArgumentErrorMsg(1, "not an iodata term"), |
| 1395 | + ); |
| 1396 | + } |
| 1405 1397 | }, |
| 1406 1398 | // End iolist_to_binary/1 |
| 1407 | - // Deps: [:lists.flatten/1] |
| 1399 | + // Deps: [:erlang.list_to_binary/1] |
| 1408 1400 | |
| 1409 1401 | // Start is_atom/1 |
| 1410 1402 | "is_atom/1": (term) => { |
| @@ -1615,6 +1607,8 @@ const Erlang = { | |
| 1615 1607 | |
| 1616 1608 | if (Type.isBinary(tail)) { |
| 1617 1609 | chunks.push(tail); |
| 1610 | + } else if (Type.isList(tail)) { |
| 1611 | + collect(tail); |
| 1618 1612 | } else { |
| 1619 1613 | Interpreter.raiseArgumentError( |
| 1620 1614 | Interpreter.buildArgumentErrorMsg(1, "not an iolist term"), |
| @@ -1,5 +1,6 @@ | |
| 1 1 | "use strict"; |
| 2 2 | |
| 3 | + import ApplicationEnv from "./erts/application_env.mjs"; |
| 3 4 | import BinaryPatternRegistry from "./erts/binary_pattern_registry.mjs"; |
| 4 5 | import NativeObjectRegistry from "./erts/native_object_registry.mjs"; |
| 5 6 | import NodeTable from "./erts/node_table.mjs"; |
| @@ -28,6 +29,7 @@ export default class ERTS { | |
| 28 29 | return $.#initPid; |
| 29 30 | } |
| 30 31 | |
| 32 | + static applicationEnv = ApplicationEnv; |
| 31 33 | static binaryPatternRegistry = BinaryPatternRegistry; |
| 32 34 | static ets = {}; |
| @@ -0,0 +1,39 @@ | |
| 1 | + "use strict"; |
| 2 | + |
| 3 | + import Type from "../type.mjs"; |
| 4 | + |
| 5 | + export default class ApplicationEnv { |
| 6 | + // Nested Map: app_string -> key_string -> value |
| 7 | + // Public for easier testing |
| 8 | + static data = new Map(); |
| 9 | + |
| 10 | + static clear() { |
| 11 | + $.data = new Map(); |
| 12 | + } |
| 13 | + |
| 14 | + static get(app, key, defaultValue) { |
| 15 | + const encodedApp = Type.encodeMapKey(app); |
| 16 | + |
| 17 | + if (!$.data.has(encodedApp)) { |
| 18 | + return defaultValue; |
| 19 | + } |
| 20 | + |
| 21 | + const appData = $.data.get(encodedApp); |
| 22 | + const encodedKey = Type.encodeMapKey(key); |
| 23 | + |
| 24 | + return appData.has(encodedKey) ? appData.get(encodedKey) : defaultValue; |
| 25 | + } |
| 26 | + |
| 27 | + static put(app, key, value) { |
| 28 | + const encodedApp = Type.encodeMapKey(app); |
| 29 | + |
| 30 | + if (!$.data.has(encodedApp)) { |
| 31 | + $.data.set(encodedApp, new Map()); |
| 32 | + } |
| 33 | + |
| 34 | + const encodedKey = Type.encodeMapKey(key); |
| 35 | + $.data.get(encodedApp).set(encodedKey, value); |
| 36 | + } |
| 37 | + } |
| 38 | + |
| 39 | + const $ = ApplicationEnv; |
Loading more files…