[HN Gopher] Porffor: A from-scratch experimental ahead-of-time J...
       ___________________________________________________________________
        
       Porffor: A from-scratch experimental ahead-of-time JS engine
        
       Author : bpierre
       Score  : 419 points
       Date   : 2024-07-30 18:55 UTC (1 days ago)
        
 (HTM) web link (porffor.dev)
 (TXT) w3m dump (porffor.dev)
        
       | rubenfiszel wrote:
       | At windmill.dev, when users deploy their code, we use Bun build
       | (which is similar to esbuild) to bundle their scripts and all
       | their dependencies into a single js file to load which improve
       | cold start and memory usage. We store the bundle on s3 because of
       | the size of the bundles.
       | 
       | If we could bundle everything to native that would completely
       | change the game since as good as bun's cold start is, you can't
       | beat running straight native with a small binary.
        
         | canadahonk wrote:
         | Hey, dev here, I agree that is an interesting application which
         | Porffor could potentially help with! Happy to chat sometime :)
        
       | solumos wrote:
       | Just out of curiosity, how does the performance (compilation +
       | runtime) compare to something like bun[0]?
       | 
       | [0] https://bun.sh/
        
       | ijustlovemath wrote:
       | I'd love to know if there's a way to compile NodeJS to native
       | libraries with this! I have a process [0], but it's a bit hacky
       | and error prone
       | 
       | [0] - https://github.com/ijustlovemath/jescx
        
       | awesomekling wrote:
       | Oliver (the main developer) just announced that they're going to
       | work full time on Porffor:
       | https://x.com/canadahonk/status/1818347311417938237
        
         | simlevesque wrote:
         | Financed by defunkt[1], GitHub cofounder and ex CEO, for an
         | undisclosed future project.
         | 
         | [1] https://news.ycombinator.com/user?id=defunkt
        
           | pityJuke wrote:
           | Also funded the Ladybird browser recently. Seems to like his
           | web.
        
             | pandemic_region wrote:
             | Not familiar with this stuff at all, is Porffor a js engine
             | that Ladybird could end up using? Or are they still writing
             | their own?
        
               | Sammi wrote:
               | Or just two separate moonshots for now.
        
               | meiraleal wrote:
               | If that happens I would thank defunkt so much. Great way
               | to spend money.
        
               | simlevesque wrote:
               | You'd have to wait before using any website using js. The
               | AOT introduces a delay. Not sure it's achievable.
        
               | giancarlostoro wrote:
               | Porffor compiles the JS to WASM, so it would be kind of a
               | waste. Though there might be no reason the two projects
               | cannot share some logic, like parsing the JS and such. I
               | kind of doubt this is why its being funded. It sounds
               | like a useful project.
        
               | stabbles wrote:
               | Since js is dynamic you can't compile all ahead of time,
               | so you need an interpreter
        
               | hyperbolablabla wrote:
               | I thought ladybird used WebKit?
        
       | obviouslynotme wrote:
       | I have thought about doing this and I just can't get around the
       | fact that you can't get much better performance in JS. The best
       | you could probably do is transpile the JS into V8 C++ calls.
       | 
       | The really cool optimizations come from compiling TypeScript, or
       | something close to it. You could use types to get enormous gains.
       | Anything without typing gets the default slow JS calls.
       | Interfaces can get reduced to vtables or maybe even straight
       | calls, possibly on structs instead of maps. You could have an Int
       | and Float type that degrade into Number that just sit inside
       | registers.
       | 
       | The main problem is that both TS and V8 are fast-moving, non-
       | standard targets. You could only really do such a project with a
       | big team. Maintaining compatibility would be a job by itself.
        
         | ch_sm wrote:
         | Somewhat related to this idea is AssemblyScript
         | https://www.assemblyscript.org
        
           | lerpgame wrote:
           | Yea I came here to say this, actually I was able to transpile
           | a few typescript files from my project into assembly using
           | GPT just for fun and it actually worked pretty well. If
           | someone simply implements a strict typescript-like linter
           | that is a subset of javascript and typescript that transpiles
           | into assemblyscript, I think that would work better for AOT
           | because then you can have more critical portions of the
           | application in AOT and other parts that are non-critical in
           | JIT and you get best of both worlds or something like that.
           | making js backwards compatible and AOT sounds way too
           | complicated.
        
         | com2kid wrote:
         | Ecmascript 4 was an attempt to add better types to the
         | language, which sadly failed a long time ago.
         | 
         | It'd be nice of TS at least allowed for specifying types like
         | integer, allowing some of the newer TS aware runtimes could
         | take advantage of the additional info, even if the main TS->JS
         | compilation just treated `const val: int` the same as `const
         | val: number`.
         | 
         | I wonder if a syntax like                   const counter:
         | Number<int>
         | 
         | would be acceptable.
        
           | THBC wrote:
           | Number is not semantically compatible with raw 64-bit
           | integer, so you might as well wish for a native
           | const counter = UInt64(42);
           | 
           | The current state of the art is                   const
           | counter = BigInt.asUintN(64, 42);
        
           | obviouslynotme wrote:
           | Yeah, that is why I said TS (or something similar). TS made
           | some decisions that make sense at the time, but do not help
           | compilation. The complexity of its typing system is another
           | problem. I'm pretty sure that it is Turing-complete. That
           | doesn't remove feasibility, but it increases the complexity
           | of compiling it by a whole lot. When you add onto this the
           | fact that "the compiler is the spec," you really get bogged
           | down. It would be much easier to recognize a sensible subset
           | of TS. You could probably even have the type checker throw a
           | WTFisThisGuyDoing flag and just immediately downgrade it to
           | an any.
        
             | com2kid wrote:
             | > I'm pretty sure that it is Turing-complete.
             | 
             | Because JS code can arbitrarily modify a type, any language
             | trying to specify what the outputs of a function can be
             | also has to be Turing complete.
             | 
             | There are of course still plenty of types that TS doesn't
             | bother trying to model, but it does try to cover even funny
             | cases like field names going from kebab-case to camelCase.
        
           | the_imp wrote:
           | With Extractors [1] (currently at Stage 1), you could define
           | something like this to work:                   const Integer
           | = {           [Symbol.customMatcher]: (value) =>
           | [Number.parseInt(value)]         }              const
           | Integer(counter) = 42.56;         // counter === 42
           | 
           | [1] https://github.com/tc39/proposal-extractors
        
         | wk_end wrote:
         | At least without additional extensions, TypeScript would help
         | less than you think. It just wasn't designed for the job.
         | 
         | As a simple example - TypeScript doesn't distinguish between
         | integers and floats; they're all just numbers. So all array
         | accesses need casting. A TypeScript designed to aid static
         | compilation likely would have that distinction.
         | 
         | But the big elephant in the room is TypeScript's structural
         | subtyping. The nature of this makes it effectively impossible
         | for the compiler to statically determine the physical structure
         | of any non-primitive argument passed into a function. This
         | gives you worse-than-JIT performance on all field access, since
         | JITs can perform dynamic shape analysis.
        
           | obviouslynotme wrote:
           | Outside of really funky code, especially code originally
           | written in TS, you can assume the interface is the actual
           | underlying object. You could easily flag non-recognized-
           | member accesses to interfaces and then degrade them back to
           | object accesses.
        
             | wk_end wrote:
             | You're misunderstanding me, I think.
             | 
             | Suppose you have some interface with fields a and c. If
             | your function takes in an object with that interface and
             | operates on the c field, what you want is to be able to do
             | is compile that function to access c at "the address
             | pointed to by the pointer to the object, plus 8" (assuming
             | 64-bit fields). Your CPU supports such addressing directly.
             | 
             | Because of structural subtyping, you can't do that. It's
             | not unrecognized member. But your caller might pass in an
             | object with fields a, _b_ , and c. This is entirely
             | idiomatic. Now c is at offset 16, not 8. Because the
             | physical layout of the object is different, you no longer
             | have a statically known offset to the known field.
        
               | obviouslynotme wrote:
               | I would bet that, especially outside of library code,
               | 95+% of the typed objects are only interacted with using
               | a single interface. These could be turned into structs
               | with direct calls.
               | 
               | Outside of this, you can unify the types. You would take
               | every interface used to access the object and create a
               | new type that has all of the members of both. You can
               | then either create vtables or monomorphize where it is
               | used in calls.
               | 
               | At any point that analysis cannot determine the actual
               | underlying shape, you drop to the default any.
        
               | pjmlp wrote:
               | Which is exactly the kind of optimizations JIT compilers
               | are able to perform, and AOT compiler can't do them
               | safely without having PGO data, and even then, they can't
               | re-optimize if the PGO happens to miss a critical path
               | that breaks all the assumptions.
        
               | fenomas wrote:
               | > Because of structural subtyping, you can't do that
               | 
               | In practice v8 does exactly what you're saying can't be
               | done, virtually all the time for any hot function. What
               | you mean to say is that typescript type declarations
               | _alone_ don 't give you enough information to safely do
               | it during a static compile step. But modern JS engines,
               | that track object maps and dynamically recompile, do what
               | you described.
        
               | wk_end wrote:
               | I mentioned this in my original comment:
               | 
               | > This gives you worse-than-JIT performance on all field
               | access, since JITs can perform dynamic shape analysis.
               | 
               | We're talking about using types to guide static
               | compilation. Dynamic recompilation is moot.
        
               | fenomas wrote:
               | Oh, I thought JIT in your comment meant a single
               | compilation. Either way, having TS type guarantees would
               | obviously make optimizing compilers like v8's stronger,
               | right? You seem to be arguing there's no value to it, and
               | I don't follow that.
        
               | wk_end wrote:
               | My claim is that the guarantees that TS provides aren't
               | strong enough to help a compiler produce stronger
               | optimizations. Types don't just magically make code
               | faster - there's specific reasons why they can make code
               | faster, and TypeScript's type system wasn't designed
               | around those reasons.
               | 
               | A compiler might be able to wring _some_ things out of it
               | (I 'm skeptical about obviouslynotme's suggestions in a
               | cousin comment, but they seem insistent) or suppress some
               | checks if you're happy with a segfault when someone did a
               | cast...but it's just not a type system like, say, C's,
               | which is more rigid and thus gives the compiler more to
               | work with.
        
           | munificent wrote:
           | I think the even bigger elephant in the room is that
           | TypeScript's type system is unsound. You can have a function
           | whose parameter type is annotated to be String and there's
           | absolutely no guarantee that every call to that function will
           | pass it a string.
           | 
           | This isn't because of `any` either. The type system itself
           | deliberately has holes in it. So any language that uses
           | TypeScript type annotations to generate faster/smaller code
           | is opening itself to miscompiling code and segfaults, etc.
        
             | wk_end wrote:
             | So - I know this in theory, but avoided mentioning it
             | because I couldn't immediately think of any persuasive
             | examples (whereas subtype polymorphism is a core, widely
             | used, wholly unrestricted property of the language) that
             | didn't involve casts or any/unknown or other things that
             | people might make excuses for.
             | 
             | Do you have any examples off the top of your head?
        
               | neongreen wrote:
               | https://counterexamples.org/ is a good collection of
               | unsoundness examples in various languages.
               | 
               | For TypeScript, they list an example with `instanceof`:
               | 
               | https://counterexamples.org/polymorphic-union-
               | refinement.htm...
               | 
               | In the playground:
               | 
               | https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9m
               | ABA...
        
               | sixbrx wrote:
               | Here's an example I constructed after reading the TS docs
               | [1] about flow-based type inference and thinking "that
               | can't be right...".
               | 
               | It yields no warnings or errors at compile stage but
               | gives runtime error based on a wrong flow-based type
               | inference. The crux of it is that something can be a Bird
               | (with "fly" function) but can also have any other
               | members, like "swim" because of structural typing (flying
               | is the _minimum_ expected of a Bird). The presence of a
               | spurious  "swim" member in the bird causes tsc to infer
               | in a conditional that checks for a "swim" member that the
               | animal must be a Fish or Human, when it is not (it's just
               | a Bird with an unrelated, non-function "swim" member).
               | type Fish = { swim: () => void };         type Bird = {
               | fly: () => void };         type Human = { swim?: () =>
               | void; fly?: () => void };              function
               | move(animal: Fish | Bird | Human) {           if ("swim"
               | in animal) {             // TSC infers wrongly here the
               | presence of "swim" implies animal must be a Fish or Human
               | onlyForFishAndHumans(animal);            } else {
               | animal;           }         }              function
               | onlyForFishAndHumans(animal: Fish | Human) {           if
               | (animal.swim) {             animal.swim(); // Error:
               | attempt to call "not-callable".           }           //
               | (receives bird which is not a Fish or Human)         }
               | const someObj = { fly: () => {}, swim: "not-callable" };
               | const bird: Bird = someObj;              move(bird);
               | // runtime error: [ERR]: animal.swim is not a function
               | 
               | [1] https://www.typescriptlang.org/docs/handbook/2/narrow
               | ing.htm...
        
               | oxidant wrote:
               | This narrowing is probably not the best. I'm not sure why
               | the TS docs suggest this approach. You should really
               | check the type of the key to be safer, though it's still
               | not perfect.                  if (typeof animal.swim ===
               | 'function') {....}
        
               | noway421 wrote:
               | Here's a simpler repro:                   type Bird = {
               | id: number };         type Human = { swim?: () => void;
               | id: number };              function move(animal: Bird |
               | Human) {             onlyForHumans(animal);          }
               | function onlyForHumans(swimmer: Human) {           if
               | (swimmer.swim) {             swimmer.swim();           }
               | }              const someObj = { id: 1, swim: "not-
               | callable" };         move(someObj);
               | 
               | `someObj` gets casted as `Bird`, then `animal` gets
               | casted as `Human`. Unfortunately unions here are indeed
               | unsound.
               | 
               | As as workaround you could add a property `type` (either
               | `"bird"` or `"human"`) to both `Bird` and `Human`, then
               | TypeScript would be able to catch it.
        
             | curtisblaine wrote:
             | It might be useful for an interpreter though. I believe
             | that in V8 you have this probabilistic mechanism in which
             | if the interpreter "learns" that an array contains e.g.
             | numbers consistently, it will optimize for numbers and
             | start accessing the array in a more performance way.
             | Typescript could be used to inform the interpreter even
             | before execution. (My supposition, I'm not an interpreter
             | expert)
        
             | g15jv2dp wrote:
             | > I think the even bigger elephant in the room is that
             | TypeScript's type system is unsound.
             | 
             | Can you name a single language that is used for high-
             | performance software and whose type system is sound? To
             | speed up the process, note that none of the obvious
             | candidates have sound type systems.
        
               | MathMonkeyMan wrote:
               | Maybe OCaml, but I haven't studied it much.
        
               | g15jv2dp wrote:
               | I doubt it's been proved to be sound. It shows up a lot
               | on https://counterexamples.org/, although if I skim the
               | issues seem to have been fixed since then.
        
               | IsTom wrote:
               | I've run a few times into messages of the sort "you can't
               | use these features together" before and I assume at least
               | sometimes these were lessons that they had to learn the
               | hard way.
        
               | mike_hearn wrote:
               | JVM bytecode is a "language" and is proven to be sound.
               | The languages that compile to that language, on the other
               | hand, are a different kettle of fish.
        
               | g15jv2dp wrote:
               | This is specifically about type systems. It's easy to
               | have a sound type system when you have no type system.
               | 
               | Also, I'm not too familiar with JVM bytecode, but if I
               | load i64 in two registers and then perform floating point
               | addition on these registers, does the type system prevent
               | me from compiling/executing the program?
               | 
               | Can you say more about "proven to be sound"? Are you
               | talking about a sound type system?
        
               | mike_hearn wrote:
               | It does have a type system.
               | 
               | https://docs.oracle.com/javase/specs/jvms/se22/html/jvms-
               | 2.h...
               | 
               | JVM is a stack not register machine and yes the type
               | system will prevent that from running. It will fail
               | verification.
        
               | skitter wrote:
               | The type checker is specified in Prolog and rejects the
               | above scenario:
               | instructionIsTypeSafe(fadd, Environment, _Offset,
               | NextStackFrame, ExceptionStackFrame) :-
               | validTypeTransition(Environment, [float, float], float,
               | StackFrame, NextStackFrame),
               | exceptionStackFrame(StackFrame, ExceptionStackFrame).
               | 
               | Fun fact: Said type system has a 'top' type that is both
               | the top type of the type system as well as the top half
               | of a long or double, as those two actually take two
               | values while everything else, including references, is
               | only one value. Made some sense when everything was 32
               | bit, less so today.
        
               | nequo wrote:
               | Scala 3 has aimed to get sound but I'm not sure how far
               | they got?
               | 
               | https://www.scala-
               | lang.org/api/3.x/docs/blog/2016/02/17/scal...
        
               | IsTom wrote:
               | I'm a little behind times on Haskell (haven't used it for
               | some years) - there always were _extensions_ that made it
               | unsound, but the core language was pretty solid.
        
               | g15jv2dp wrote:
               | Look at the last example in there:
               | https://counterexamples.org/polymorphic-references.html
        
               | IsTom wrote:
               | Well, it does use unsafePerformIO. It's not particularly
               | horrible most of the time, but in this case it obviously
               | is.
        
               | munificent wrote:
               | Java, C#, Scala, Haskell, and Dart are all sound as far
               | as I know.
               | 
               | Soundness in all of those languages involves a mixture of
               | compile-time and runtime checks. Most of the safety comes
               | from the static checking, but there are a few places
               | where the compiler defers checking to runtime and inserts
               | checks to ensure that it's not possible to have an
               | expression of type T successfully evaluate to a value
               | that isn't an T.
               | 
               | TypeScript doesn't insert any runtime checks in the
               | places where there are holes in the static checker, so it
               | isn't sound. If it wasn't running on top of a JavaScript
               | VM which is dynamically typed and inserts checks
               | everywhere, it would be entirely possible to segfault,
               | violate memory safety, etc.
        
           | pjmlp wrote:
           | Such Typescript already exists, Static Typescript,
           | 
           | https://makecode.com/language
           | 
           | Microsoft's AOT compiler for MakeCode, via C++.
        
             | mananaysiempre wrote:
             | The 2019 paper[1] says: "STS primitive types are treated
             | according to JavaScript semantics. In particulars, all
             | numbers are logically IEEE 64-bit floating point, but
             | 31-bit signed tagged integers are used where possible for
             | performance. Implementation of operators, like addition or
             | comparison, branch on the dynamic types of values to follow
             | JavaScript semantics[.]"
             | 
             | [1]: https://www.microsoft.com/en-
             | us/research/publication/static-...
        
           | cprecioso wrote:
           | > A TypeScript designed to aid static compilation likely
           | would have that distinction.
           | 
           | AssemblyScript (https://www.assemblyscript.org/) is a
           | TypeScript dialect with that distinction
        
             | mananaysiempre wrote:
             | It's advertised as that, and it's a cool project, but while
             | it's definitely a statically typed language that reuses
             | TypeScript syntax, it's not clear to me just what subset of
             | the actual TypeScript type system is supported. That's
             | necessarily bad--TypeScript itself is very unclear about
             | what its type system actually is. I just think the tagline
             | is misleading.
        
               | eyelidlessness wrote:
               | Probably a better way to think about AssemblyScript is
               | first as a DSL for WASM, and second as providing a subset
               | of TS syntax and authoring semantics to achieve that. The
               | type system is closer to TS than the syntax and
               | semantics. At least that was my experience when I
               | explored it some time back.
        
         | refulgentis wrote:
         | You say you have "thought about doing this"..."[but] you can't
         | get much better performance", then describe the approach
         | requiring things that are described first-thing, above the
         | fold, on the site.
         | 
         | Did the site change? Or am I missing something? :)
        
         | bobvarioa wrote:
         | Contributor to Porffor here! I actually disagree, there's quite
         | a lot that can be improved in JS during compile time. There's
         | been a lot of work creating static type analysis tools for JS,
         | that can do very very thorough analysis, an example that comes
         | to mind is [TAJS](https://www.brics.dk/TAJS/) although its
         | somewhat old.
        
           | attractivechaos wrote:
           | > _there 's quite a lot that can be improved in JS during
           | compile time_
           | 
           | I wonder how much performance gain you expect to achieve. For
           | simple CPU-bounded tasks, C/Rust/etc is roughly three times
           | as fast as v8 and Julia, which compiles full scripts and has
           | good type analysis, is about twice as fast. There is not much
           | room left. C/Rust/etc can be much faster with SIMD, multi-
           | threading and fine control of memory layout but an AOT JS
           | compiler might not gain much from these.
        
             | itsTyrion wrote:
             | Honestly, I'm fine with only some speed up compared to V8,
             | it's already pretty fast... My issue with desktop/mobile
             | apps using web tech (JS) is mostly the install size and RAM
             | hunger.
        
               | mst wrote:
               | [raises hand] I'd be fine with no speedup at all if I can
               | get more reasonable RAM usage and an easily linkable .so
               | out of the deal.
        
               | attractivechaos wrote:
               | The "node" binary on my laptop is 45MB in size. I guess
               | the browser component may take more disk space than JS
               | runtime. Similarly, I am not sure whether JS runtime or
               | webpage rendering takes more RAM. If it is the latter, an
               | AOT compiler won't help much.
        
             | jitl wrote:
             | In my mind, the big room for improvement is eliminating the
             | cost to call from JS into other native languages. In
             | node/V8 you pay a memcopy when you pass or return a string
             | from C++ land. If an ahead of time compiler for JS can use
             | escape analysis or other lifetime analysis for string or
             | byte array data, you could make I/0 or at least writes from
             | JavaScript to, for example, sqlite, about twice as fast.
        
         | myko wrote:
         | Dart, maybe, but it lost
        
         | singpolyma3 wrote:
         | You can do inference and only fall back to Dynamic/any when
         | something more specific can't be globally inferred in the
         | program. For an optimization pass this is an option.
        
       | syrusakbary wrote:
       | It's awesome to see how more JS runtimes try to approach Wasm.
       | This project reminds me to Static Hermes (the JS engine from
       | Facebook to improve the speed of React Native projects on iOS and
       | Android).
       | 
       | I've spent a bit of time trying to review each, so hopefully this
       | analysis will be useful for some readers. What are the main
       | commonalities and differences between Static Hermes and Porffor?
       | * They both aim for JS test262 conformance [1]       * Porffor
       | supports both Native and Wasm outputs while Static Hermes is
       | mainly focused on Native outputs for now       * Porffor is self-
       | hosted (Porffor is written in pure JS and can compile itself),
       | while Static Hermes relies on LLVM       * Porffor currently
       | doesn't support async/promise/await while Static Hermes does
       | (with some limitations)       * Static Hermes is written in C++
       | while Porffor is mainly JS       * They both support TypeScript
       | (although Static Hermes does it through transpiling the TS AST to
       | Flow, while Porffor supports it natively)       * Static Hermes
       | has a fallback interpreter (to support `eval` and other hard-to-
       | compile JS scenarios), while Porffor only supports AOT compiling
       | (although, as I commented in other thread here, it maybe be
       | possible to support `eval` in Porffor as well)
       | 
       | In general, I'm excited to see if this project can gain some
       | traction so we can speed-up Javascript engines one the Edge!
       | Context: I'm Syrus, from Wasmer [3]
       | 
       | [1] https://github.com/facebook/hermes/discussions/1137
       | 
       | [2] https://github.com/tc39/test262
       | 
       | [3] https://wasmer.io
        
         | jonathanyc wrote:
         | Just wanted to say I really appreciated the high-quality
         | comparison. How something compares to existing work is my #1
         | question whenever I read an announcement like this.
        
           | syrusakbary wrote:
           | Thanks!
        
         | wdb wrote:
         | You make it sound bad to rely on LLVM.
        
           | bangaladore wrote:
           | Yeah... It is unclear to me how not using LLVM is a good
           | thing. You'd inherit millions of man-hours of optimization
           | work, code gen, and general thought process.
           | 
           | Is there a technical reason why?
        
             | fabrice_d wrote:
             | In this case, being self contained will help implementing
             | things like `eval()` and `Function()` since Porffor can
             | self-host. That would be much harder with a LLVM based
             | solution.
        
         | bobvarioa wrote:
         | Contributor for Porffor here! I think this is a great
         | comparison, but Porffor does technically support promises,
         | albeit _synchronously_. It 's a similar approach to Kiesel,
         | https://kiesel.dev/.
        
           | frutiger wrote:
           | Not sure where you mean by synchronously but if you mean what
           | I think you mean then that is not correct behaviour. This is
           | important to ensure predicatibility.
           | 
           | Eg.                   Promise.then(() => console.log("a"));
           | console.log("b")
           | 
           | must log ["b", "a"] and not ["a", "b"].
        
             | canadahonk wrote:
             | This type of test does work as expected. The "sync" means
             | that it does not feature a full event loop (yet) so cannot
             | easily support async I/O or some more "advanced" use cases.
        
               | spankalee wrote:
               | There's a WASI async functions proposal I think? Are you
               | looking at supporting that so you don't have to bring
               | your own event loop?
        
             | cowsandmilk wrote:
             | JavaScript doesn't make the guarantee you are claiming here
        
               | fwip wrote:
               | What do you mean? Does JavaScript allow the `then` of a
               | promise to execute before the contents of the promise?
        
               | jitl wrote:
               | Yes, it does. Promise continuations always run in the
               | micro task queue per the standard. I guess if someone
               | mutates the promise prototype it's not guaranteed, but
               | the spec does guarantee this order
        
         | canadahonk wrote:
         | Good comparison and thanks! A few minor clarifications: -
         | Porffor isn't fully self-hosted _yet_ but should be possible
         | hopefully! It does partially compile itself for builtins (eg
         | Array.prototype.filter, Math.sin, atob, ...) though. - As of
         | late, Porffor does now support basic async /promise/await! Not
         | very well yet though.
        
         | tmikov wrote:
         | For the record, Static Hermes fully supports compiling JS to
         | WASM. We get it basically for free, because it is an existing
         | LLVM backend. See
         | https://x.com/tmikov/status/1706138872412074204 for example.
         | 
         | Admittedly, it is not our focus, we are focusing mainly on
         | React Native, where WASM doesn't make sense.
         | 
         | The most important feature of Static Hermes is our type
         | checker, which guarantees runtime soundness.
         | 
         | Porffor is very interesting, I have been watching it for some
         | time and I am rooting for it.
        
           | syrusakbary wrote:
           | Thanks for the correction Tzvetan! Keep up the great work in
           | Static Hermes
        
       | saagarjha wrote:
       | What happens when someone calls eval?
        
         | syrusakbary wrote:
         | Since Porffor can compile itself (you can run the compiler
         | inside of Porffor), any calls to eval could be compiled to Wasm
         | (via executing the Porffor compiler in Porffor JS engine) and
         | executed performantly on the same JS context *
         | 
         | *or at least, in theory
        
           | tasty_freeze wrote:
           | I haven't used it, but reading their landing page, Porffor
           | says their runtime is vastly smaller because it is AOT. If
           | the compiler had to be bundled with the executable, then the
           | size of the executable would grow much larger.
        
             | spartanatreyu wrote:
             | Yeah, but you'd only need to include Porffor into compiled
             | code if it used eval.
             | 
             | And most devs stay away from eval for well-deserved
             | security reasons.
        
           | concerndc1tizen wrote:
           | That wouldn't work: The Wasm spec does not allow for
           | modifying an already running program (e.g. JIT).
           | 
           | AFAIK the only option is to include an interpreter.
        
             | syrusakbary wrote:
             | You don't need to modify an already running program, you
             | can plug new functions into an existing Wasm program via a
             | table, and even attach variables via globals or function
             | arguments.
             | 
             | I'd recommend checking the work on making SpiderMonkey emit
             | Wasm as a backend
        
         | david2ndaccount wrote:
         | With a string literal it works, with a dynamic string it just
         | gives an undefined reference error to eval.
        
         | canadahonk wrote:
         | For now, unless it is given a literal string (eg `eval('42')`)
         | eval just won't work.
        
       | THBC wrote:
       | This seems like an opaque supply chain attack waiting to happen.
        
       | nick_g wrote:
       | I'm a bit suspicious of the versioning scheme described here[0]
       | 
       | If some change were required which introduced a regression on
       | some Test262 tests, it could cause the version number to regress
       | as well. This means Porffor cannot have both a version number
       | which increases monotonically and the ability to introduce
       | necessary changes which cause Test262 regressions
       | 
       | [0] https://github.com/CanadaHonk/porffor?tab=readme-ov-
       | file#ver...
        
         | derdi wrote:
         | Presumably the idea is that any work that causes Test262
         | regressions is temporary, takes place in a separate branch, and
         | is only merged to main once the branch also contains all the
         | necessary fixes to make the regressions go away again. A new
         | version number would only be used once that merge happens.
        
           | canadahonk wrote:
           | Exactly. The versioning system is definitely unique and
           | controversial, but I think it fits for a fast moving project
           | like this, so I don't have to really consider versioning
           | which could slow development. When it becomes more stable,
           | I'll likely move to a more traditional semver scheme from
           | 1.0.
        
       | rvnx wrote:
       | Seems like the same idea that Facebook had with PHP which was to
       | transpile PHP to C.
       | 
       | It was called hiphop-php, then they eventually gave up, before
       | creating hhvm on a complete new concept.
        
         | pjmlp wrote:
         | Historically the sequence is a bit different.
         | 
         | After HHVM proved that its JIT compilation engine was faster
         | than their HipHop AOT attempt, did they decided to focus only
         | on HHVM going forward.
        
       | Borkdude wrote:
       | I got "TodoError: no generation for ImportDeclaration!" for this
       | script:
       | 
       | import * as squint_core from 'squint-cljs/core.js';
       | console.log("hello");
        
       | mproud wrote:
       | "Purple" in Welsh
        
         | zem wrote:
         | with etymology from the greek for purple; the english
         | "porphyry" (a purple mineral) is probably the commonest word
         | with the same root.
        
       | CharlesW wrote:
       | What subtleties am I missing that makes "ahead-of-time JS engine"
       | a better description than "JS-to-Wasm compiler"? (If it's mostly
       | a framing strategy, that's cool too.)
        
         | chillfox wrote:
         | There are already projects that does JS-to-WASM by bundling a
         | JS interpreter. So, it's likely to make the difference to those
         | clearer.
        
           | canadahonk wrote:
           | Yep! Also as it is technically more of an engine/runtime
           | (sometimes) than "just" a compiler, folks in the JS space are
           | more familiar with engine as a term :)
        
       | FpUser wrote:
       | I find this very interesting. Keep it up and bring it to
       | production shape
        
       | WatchDog wrote:
       | How does this compare to quickJS, which can also compile JS to
       | native code(with a C compiler)
        
         | spacechild1 wrote:
         | I _think_ QuickJS only compiles to bytecode and then embeds it
         | together with the interpreter in an executable. The JS itself
         | is still interpreted. Others please correct me if I 'm wrong.
        
       | xiaodai wrote:
       | Stop trying to retrofit garbage on garbage. Go direct to WebAsm
       | already
        
       | Sytten wrote:
       | Its refreshing to see all the various JS engines that are out
       | there for various usecases.
       | 
       | I have been working on providing quickjs with more node
       | compatible API through llrt [1] for embedding into applications
       | for plugins.
       | 
       | [1] https://github.com/awslabs/llrt
        
       | userbinator wrote:
       | _Porffor can compile to real native binaries without just
       | packaging a runtime like existing solutions._
       | 
       | Any language that allows generating and interpreting its own code
       | at runtime will have the "eval problem". From some other comments
       | here, it sounds like Porffor's solution is to simply ignore it.
        
       | brundolf wrote:
       | There's a subset of JS that's trivially compilable, it's the long
       | tail of other stuff that's hard. But cool to see research
       | happening on where that boundary lies and how much benefit can be
       | had for that subset
        
       | vanderZwan wrote:
       | I unironically appreciate that it supports String.blink. It's
       | always a good sign if the developer has a sense of humor and
       | playfulness.
        
         | chrismorgan wrote:
         | If it's interested in behaving as though "the ECMAScript host
         | is a web browser", _of course_ it does, it's part of the spec:
         | https://tc39.es/ecma262/multipage/additional-ecmascript-
         | feat.... And given how trivial it is (function() { return
         | "<blink>" + this + "</blink>"; }), it makes a fair amount of
         | sense to implement it even if the ECMAScript host is not a web
         | browser, in which case it's optional (see the top of the linked
         | document). I wouldn't expect it to have any association with "a
         | sense of humor or playfulness" whatsoever.
        
           | vanderZwan wrote:
           | The reason I would argue that it _does_ imply a sense of
           | humor is that on any web browser that supports WASM, the
           | <blink> tag itself has been deprecated and non-functional for
           | ages. In fact, it doesn't even have an entry on MDN, only an
           | indirect reference through String.blink()
           | 
           | https://developer.mozilla.org/en-
           | US/docs/Web/JavaScript/Refe...
        
             | chrismorgan wrote:
             | <blink>, sure, but String.prototype.blink is still part of
             | the spec, and unlike the HTML Standard, the ECMAScript
             | specs are much more pseudocode that you largely just copy
             | and turn into actual code as necessary, to the point that,
             | if as an ECMAScript host it's playing the web browser, I'd
             | be extremely surprised (as in, "wait, _what_!? This is
             | literally the weirdest technical thing I've seen all year,
             | maybe this _decade_ ") if that one method _wasn't_ there.
             | When you're implementing specs written like this, you just
             | do it all; you don't--you _never_ --pick and choose based
             | on "that thing is obsolete and no one uses it anyway".
        
         | csjh wrote:
         | It's in test262, so it basically has to be supported for the
         | project to accomplish its goals.
        
       | giancarlostoro wrote:
       | The most interesting bit about Porffor in my eyes is it lets
       | JavaScript compete with something like Blazor (or allows JS to
       | stand its ground), which kind of makes using any JS in your
       | project redundant, since all your front-end logic can be done in
       | C#. The reason I say this is, because obviously, there are JS
       | devs, but if WASM tooling in other languages grows it will make
       | JS redundant or feel incomplete / outcompeted.
       | 
       | I wont be surprised to see a SPA framework that uses Porffor once
       | it is more mature, or even the major ones using it as part of
       | their tooling.
       | 
       | WASM is the next step after SPA's essentially.
       | 
       | If you have never touched Blazor, I recommend you check it out
       | via youtube video if you don't do any C#, it is impressive. Kudos
       | to Microsoft for it. I have had 0 need or use for JavaScript
       | since using it.
        
         | tempodox wrote:
         | That sounds like we could finally get rid of the worst
         | programming language to ever reach as widespread adoption as JS
         | has today. I can't wait!
        
       ___________________________________________________________________
       (page generated 2024-07-31 23:00 UTC)