Live data from Hacker News

Porffor: A from-scratch experimental ahead-of-time JS engine

porffor.dev

31–40 of 148 posts

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#31
post #14

Earlier quoted context omitted.

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. T…

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.

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.

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#32
post #14

Earlier quoted context omitted.

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. T…

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/small…

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?

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#33
post #32

Earlier quoted context omitted.

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/small…

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?

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/GYVwdgxgLglg9mABA...

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#34

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 test26…

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.

[deleted]

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#35
post #31

Earlier quoted context omitted.

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.

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 ca…

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.

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#36

Oliver (the main developer) just announced that they’re going to work full time on Porffor: https://x.com/canadahonk/status/1818347311417938237

Financed by defunkt[1], GitHub cofounder and ex CEO, for an undisclosed future project. [1] https://news.ycombinator.com/user?id=defunkt

Also funded the Ladybird browser recently. Seems to like his web.

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#37
post #10

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 may…

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 would be ac…

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

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#38
post #14

Earlier quoted context omitted.

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. T…

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/small…

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)

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#39
post #10

Earlier quoted context omitted.

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 would be ac…

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 dow…

> 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.

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#40

What happens when someone calls eval?

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

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.
Post reply on HN