Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ES5/6/7/8/etc wishlist #1302

Open
gfwilliams opened this issue Jan 9, 2018 · 50 comments
Open

ES5/6/7/8/etc wishlist #1302

gfwilliams opened this issue Jan 9, 2018 · 50 comments
Labels

Comments

@gfwilliams
Copy link
Member

ES5/ES6 features that aren't in Espruino but that would be useful... I'll do one post per feature, and please use the add reaction button to vote on each one.

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 9, 2018

  • String.startsWith/endsWith/includes - would be trivial to add

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 9, 2018

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 9, 2018

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 9, 2018

  • Array.find/findIndex - find by executing a function for comparison

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 9, 2018

  • JS classes - probably not too painful, and seems like it'd tidy up a lot of code

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 9, 2018

  • Default argument values

Right now we store function arguments in their own variables in the function:

>function test(a,b) { print(a,b); }
=function (a,b) { ... }
>trace(test)
#1448[r1,l1] Function {
  #1273[r1,l2] Name Param "\xFFa"     undefined
  #1177[r1,l2] Name Param "\xFFb"     undefined
  #1508[r1,l2] Name String [1 blocks] "\xFFcod"    #1516[r1,l0] String [2 blocks] "print(a,b);"
}

Potentially if we stored them as a single string it'd be more memory efficient in all but the single-argument case, and would allow us to have default arguments, eg a,b=42

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 9, 2018

  • Array.includes - easy, just indexOf()>=0

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 9, 2018

  • Object.entries and Object.values

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 9, 2018

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 9, 2018

  • async/await - although this it likely too difficult to implement in Espruino as it is

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 9, 2018

@FlowerOfLife
Copy link

FlowerOfLife commented Feb 20, 2018

@gfwilliams
Copy link
Member Author

@gfwilliams
Copy link
Member Author

@jjok
Copy link

jjok commented Feb 26, 2018

import and export?

@vshymanskyy
Copy link
Contributor

Ok, I was actually able to get async/await working on Espruino (using Babel transpiler and some hacking).
Here is a full example: https://github.com/vshymanskyy/espruino-await
Looks pretty usable, but a native implementation would be so much better ;)
What do you think?

@louisvangeldrop
Copy link

Webassembly support?

@andrewwakeling
Copy link
Contributor

andrewwakeling commented Apr 3, 2018

I did a similar exercise to @vshymanskyy and got various modern JS features working using Webpack/Babel. See: https://github.com/andrewwakeling/espruino-webpack-babel-sample

Although supporting modern JS features (spread, destructuring) out-of-the-box would be convenient, these things are somewhat trivial to support if you compile the code.

I would love to see fixes/improvement to ensure that more modern JS features work correctly and more reliably (after compilation). It would also be great to see improvements that would reduce the size of the compiled code. e.g. Transpiling async/await increases the bundle size a fair amount. I understand that supporting async/await isn't trivial, so I'm not pushing for this.

@opichals
Copy link
Contributor

opichals commented May 16, 2018

  • Enhanced Object Literals (methods and shorthand now allowed in object literals, but the more advanced bits are missing)

This would allow for shorter syntax which is quite important for Espruino.

@gfwilliams
Copy link
Member Author

gfwilliams commented May 18, 2018

Note: For many use-cases you can get acceptable map or set-like functionality using just standard JavaScript objects ({})

@gfwilliams
Copy link
Member Author

gfwilliams commented May 18, 2018

@opichals
Copy link
Contributor

opichals commented Jun 6, 2018

This would be nice way to deal with stuff like case-insensitive Object key lookup in a standard way.

@loganpowell
Copy link

Hi, sorry to chime in as a complete stranger, but given the whole Babel ecosystem, wouldn't it be more prudent to get complete ES5 feature coverage and then let users compile their ES6+ code down to ES5 before running through Espruino? I'm sorry if this is a naive question that has a straight answer somewhere obvious.

@gfwilliams
Copy link
Member Author

gfwilliams commented Jan 7, 2019

Hey, no problem. It's actually pretty trivial to set up your own Babel + Espruino CLI toolchain right now if you want to - just not inside the IDE.

The problem with Babel on Espruino is really:

  • if you write code on the left-hand side of the IDE it goes straight to Espruino, so you don't get to use babel - in fact babelified code will probably be harder to interact with.
  • Without a lot of work it'd also mess up the debugging functionality
  • In fact in quite a few cases babel will significantly increase the amount of code used on the micro itself

It's not a reason not to do it, but I think there's still a place for implementing this stuff natively, especially where doing so reduces the amount of code storage needed.

@loganpowell
Copy link

loganpowell commented Jan 7, 2019 via email

@jjok
Copy link

jjok commented Jan 7, 2019

@loganpowell Is there anything in particular from ES5 that you've noticed is missing? Maybe open a separate feature request, if there is something.

EDIT: Oh. This is also the ES5 Wishlist. Just add it here, I guess.

@Dabolus
Copy link
Contributor

Dabolus commented Feb 3, 2023

This would allow working with values that do not fit into a standard JS number. In the past, 64 bit numbers were supported, but they were (rightfully) removed to make Espruino more JS-compatible. This is the current status:

const testNumber = 18446744073709551615; // 2^64 - 1, max value for a uint64
console.log(testNumber); // Logs -1 instead of 18446744073709551615

const testBigInt = 18446744073709551615n; // Syntax error

Note that (for obvious reasons) this feature cannot be transpiled using babel/typescript/esbuild/whatever.

@gfwilliams
Copy link
Member Author

Thanks - but I think BigInt probably won't make it in, as it would involve quite a lot of extra code to handle the arbitrary precision maths needed (I believe implementing just 64 bit maths isn't good enough?). I guess there is a possibility that Espruino could be extended with some non-standard operator overloading function such that someone could then make a BigInt class in JS though - that could be a possibility.

@Dabolus
Copy link
Contributor

Dabolus commented Feb 13, 2023

Looks like it could be an easy win. I tried opening a draft PR here, but I didn't test it yet.

@Dabolus
Copy link
Contributor

Dabolus commented Feb 15, 2023

New array static methods:

Both for standard arrays and typed arrays (Uint8Array.of, Uint8Array.from, etc.)

Array.of consists in simply creating and returning an array from the provided variadic arguments, while Array.from consists in creating an array from an iterable or an array-like, optionally providing a map function as second argument.

@Dabolus
Copy link
Contributor

Dabolus commented Feb 15, 2023

I noticed that many modern transpilers/bundlers use it and take its existence for granted, so right now some transpiled code doesn't work unless you add additional quirks and polyfills to make this function available before the transpiled code generated by the transpiler/bundler. Given that Object.getOwnPropertyDescriptor already exists, it should be trivial to also implement this one. Example shim:

Object.getOwnPropertyDescriptors = function getOwnPropertyDescriptors (obj) {
  if (obj === null || obj === undefined) {
    throw new TypeError('Cannot convert undefined or null to object');
  }

  const protoPropDescriptor = Object.getOwnPropertyDescriptor(obj, '__proto__');
  const descriptors = protoPropDescriptor ? { ['__proto__']: protoPropDescriptor } : {};

  for (const name of Object.getOwnPropertyNames(obj)) {
    descriptors[name] = Object.getOwnPropertyDescriptor(obj, name);
  }

  return descriptors;
}

@Dabolus
Copy link
Contributor

Dabolus commented Feb 15, 2023

This should also be something easy to implement in its simplest form, as something similar already exists on Espruino:

  • new TextDecoder().decode(uint8Array) is functionally equivalent to E.toString(uint8Array)
  • new TextEncoder().encode(str) is functionally equivalent to new Uint8Array(E.toArrayBuffer(str))

Sorry for the noise by the way; I'm trying to run an SDK built for the web on Espruino, so I'm writing down all the things I discover while doing it that might be beneficial to implement in the Espruino core to increase compatibility with the web.
I'm also having a look at how Espruino is implemented because I would like to contribute directly, even though I'm not exactly a C expert.

@gfwilliams
Copy link
Member Author

Thanks! These look like good ideas. getOwnPropertyDescriptors seems easy and a good one to add if transpilers expect it. Array.of looks neat too - interestingly we have E.toUint8Array/etc which does similar things, so the code for that could be re-used.

It's a shame there isn't String.of as that would be a very neat fix for issues we are currently having with E.toString doing a bit more than you might expect from the name.

TextEncoder/Decoder might be a bit trickier to do in a spec compliant way - I think the closest might be E.decodeUTF8

@Dabolus
Copy link
Contributor

Dabolus commented Mar 2, 2023

Since version 15, Node.js fully supports it too.

Considering that every object in Espruino is an EventEmitter, probably the easiest way to (kinda) support this would be to simply expose the same emit, on, and removeListener functions just with a different name (respectively dispatchEvent, addEventListener, and removeEventListener).

I also noticed that Espruino actually already has an addEventListener function implemented here, but it isn't exposed as of today.

This is the shim I'm currently using to use a library that makes use of EventTarget + CustomEvent, in case someone else needs it (non-spec compliant, as it doesn't accept options such as passive, once, etc., but does its work):

class Event {
  constructor(type, options) {
    this.type = type;
    this.bubbles = !!(options && options.bubbles);
    this.cancelable = !!(options && options.cancelable);
    this.composed = !!(options && options.composed);
  }
}

class CustomEvent extends Event {
  constructor(type, options) {
    super(type, options);
    this.detail = options && options.detail;
  }
}

// Every object is Espruino is a Node.js EventEmitter
// See: https://www.espruino.com/Reference#l_Object_on
class EventTarget {
  dispatchEvent(event) {
    return this.emit(event.type, event);
  }

  addEventListener(type, listener) {
    this.on(type, listener);
  }

  removeEventListener(type, listener) {
    this.removeListener(type, listener);
  }
}

// Then, use it like this
class Test extends EventTarget {
  test() {
    this.dispatchEvent(new CustomEvent('test', { detail: { a: 'b' }}));
  }
}

const t = new Test();

t.addEventListener('test', e => console.log('received test event:', e));
t.test();

// As soon as the event is emitted, this is logged in the console:
// received test event: CustomEvent: {
//   "type": "test",
//   "bubbles": false, "cancelable": false, "composed": false,
//   "detail": {
//     "a": "b"
//    }
//  }

@gfwilliams
Copy link
Member Author

Thanks - yes, I guess that could be an idea, although it's not actually adding any functionality that's not there already so I feel like this is low priority.

Especially on the more constrained devices like micro:bit 1 where we're having to cut features out to fit, it seems a step back to have to remove existing features just so we can duplicate some functions but with longer names.

already has an addEventListener function implemented here

That one is for use internally (it uses char* as an argument) so it can't actually be exposed to JS - it just happens to have the same name :)

@atjn
Copy link
Contributor

atjn commented Dec 29, 2023

These are just different ways of doing a console.log, which is already supported. At a minimum, it would be nice to have them implemented as an alias of console.log, since many programs use them, and will currently fail to run because of that.

If you want to do it properly You should implement some icons and color styling for the different log types. It would also be ideal if warn and error have some sort of trace. If you implement the trace, you might as well also implement `console.trace`.

Here is how Firefox does it. (in the order: debug, info, warn, error, trace)
image

Here is how Chrome does it:
image

Any other methods from the console would also be cool, but they might be harder to implement.

@gfwilliams
Copy link
Member Author

Thanks - just done. Aliasing doesn't add a bunch of extra memory use. Stack traces are much harder to accomplish, and while we could send extra formatting chars and the IDE will parse them, I guess if someone is connected to the Bangle via something that's not the IDE, it could cause problems

@mariusGundersen
Copy link
Contributor

Could console.warn and console.error set the background and text color using ansi codes?

@atjn
Copy link
Contributor

atjn commented Jan 2, 2024

I believe that is what gfwilliams is addressing at the end of their comment. Maybe some programs will not be able to handle ANSI codes, so better to not use them. I don't know enough about espruino to debate that.

@gfwilliams
Copy link
Member Author

Exactly, yes. Potentially if you really wanted this, it's pretty easy to add a file (maybe to .boot1 which should work on all Espruino devices) that overrides console.warn/etc with one that adds the relevant escape sequences.

@atjn
Copy link
Contributor

atjn commented Feb 26, 2024

It can be polyfilled/implemented in Espruino with the single line global.globalThis = global. (it can be overwritten by design)
This is very useful for programs because it is guaranteed to exist in all JS contexts, whereas global is non-standard and not supported in all JS engines, and this is not always supported or does not point to the global object.

@gfwilliams
Copy link
Member Author

Just done

@thyttan
Copy link
Contributor

thyttan commented Apr 2, 2024

  • String.replaceAll

I've tried to use it maybe three times now over the course of a couple of months. The functionality is already achievable e.g. with String.replace using a replace function as second argument - but I have not managed to learn that yet...

@gfwilliams
Copy link
Member Author

gfwilliams commented Apr 3, 2024

Thanks - that looks like a good one to have. If you're using a regex I believe the g postfix works fine already: "stringing".replace(/i/g,"_")

But it's nice not to have to rely on regex

edit: Just added. Looks like the replace function is only supported right now when using RegExp as the first argument, but changing that to make it work nicely while not using loads of extra flash memory on devices without much available looks like a big task so I'm not doing it at the moment

@Lemenxzy
Copy link

Lemenxzy commented Jun 11, 2024

I would like to kindly request, if it's not too much trouble, the support for async and await functions. Your assistance would be greatly appreciated. Thank you ever so much

@gfwilliams
Copy link
Member Author

gfwilliams commented Jun 12, 2024

There's already an item for this above if you look - but as mentioned there it's extremely difficult (if not impossible) to implement in Espruino due to the fact it doesn't work by compiling down to bytecode. I don't think realistically there's a way to make this work except in very basic cases (await used in the main basic block of the function). If you want async/await it's probably best to look at transpiling

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests