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

Strictness, caller and arguments spec compliancy #1582

Closed
leotm opened this issue Dec 13, 2024 · 8 comments
Closed

Strictness, caller and arguments spec compliancy #1582

leotm opened this issue Dec 13, 2024 · 8 comments
Labels
bug Something isn't working fixed-in-sh Fixed in SH but "wontfix" in Hermes

Comments

@leotm
Copy link

leotm commented Dec 13, 2024

Bug Description

When repairing Hermes intrinsics via Secure EcmaScript to reach spec compliancy

we detect non-standard/deprecated .caller and .arguments properties on several intrinsics

so have an expedient temp fix (proposed) to account for these

but would love to understand what Hermes is trying to do and the path towards spec-compliancy cc @tmikov @erights

some more details

https://github.com/facebook/hermes/blob/main/doc/Features.md#miscellaneous-incompatibilities

arguments changes in non-strict mode will not sync with named parameters

ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode#no_syncing_between_parameters_and_arguments_indices

hermes

CallResult<HermesValue>
throwTypeError(void *ctx, Runtime &runtime, NativeArgs) {
static const char *TypeErrorMessage[] = {
"Restricted in strict mode",
"Dynamic requires are not allowed after static resolution",
};
uint64_t kind = (uint64_t)ctx;
assert(
kind < (uint64_t)TypeErrorKind::NumKinds &&
"[[ThrowTypeError]] wrong error kind passed as context");
return runtime.raiseTypeError(TypeErrorMessage[kind]);
}

print('function properties');
// CHECK-LABEL: function properties
print(typeof strict, typeof nonStrict);
// CHECK-NEXT: function function
print(nonStrict.caller, nonStrict.arguments);
// CHECK-NEXT: undefined undefined
try { print(strict.caller); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted in strict mode
try { print(strict.arguments); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted in strict mode
var bound = nonStrict.bind(42);
try { print(bound.caller); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted in strict mode
try { print(bound.arguments); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted in strict mode

static_h

CallResult<HermesValue>
throwTypeError(void *ctx, Runtime &runtime, NativeArgs) {
static const char *TypeErrorMessage[] = {
"Restricted property cannot be accessed",
"Dynamic requires are not allowed after static resolution",
};
uint64_t kind = (uint64_t)ctx;
assert(
kind < (uint64_t)TypeErrorKind::NumKinds &&
"[[ThrowTypeError]] wrong error kind passed as context");
return runtime.raiseTypeError(TypeErrorMessage[kind]);
}

print('function properties');
// CHECK-LABEL: function properties
print(typeof strict, typeof nonStrict);
// CHECK-NEXT: function function
try { print(nonStrict.caller); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted property cannot be accessed
try { print(nonStrict.arguments); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted property cannot be accessed
try { print(strict.caller); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted property cannot be accessed
try { print(strict.arguments); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted property cannot be accessed
var bound = nonStrict.bind(42);
try { print(bound.caller); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted property cannot be accessed
try { print(bound.arguments); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted property cannot be accessed
try { print(Function.prototype.caller); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted property cannot be accessed
try { print(Function.prototype.arguments); } catch(e) { print('caught', e.name, e.message); }
// CHECK-NEXT: caught TypeError Restricted property cannot be accessed

@tmikov
Copy link
Contributor

tmikov commented Dec 14, 2024

Hi, I am not exactly sure what you are asking.

If there are specific bugs, please file issues for them (or submit PRs). We will address them, except the ones that we have explicitly said we do not support, though nothing is set in stone. For example, we have a PR from Snapchat adding support for with.

Admittedly, some issues are not high priority, like allowing assignment to arguments in sloppy mode, or ones that provide no functionality like .caller and .arguments (although I am not sure what the issue is there).

BTW, the latest most compliant implementation is in the static_h branch.

but would love to understand what Hermes is trying to do and the path towards spec-compliancy

Spec compliance is important to us, but due to internal constraints and priorities, we have taken a pragmatic approach. We separate compliance into different axis:

  1. Lack of functionality from modern versions of the spec (like for await ... of).
  2. Lack of library features from modern versions of the spec.
  3. Lack of support of esoteric sloppy mode features (e.g. syncing between named args and arguments[]).
  4. Incorrect implementation likely to cause bugs.
  5. Incorrect implementation less likely to cause a bug (like modifying a function expression name inside the expression).
  6. Early reporting of errors that ought to be reported at runtime.

Clearly, among these 4) is by far the most important and we try to always fix these urgently. The rest of the list has to compete with other priorities. We are not a self-governing organization, we are employees of a corporation...

We have recently made great strides with 1, 6 and somewhat 5. We added native support for block scoping, TDZ, classes, and more is coming. We have improved sloppy mode identifier resolution in corner cases and, again, more is coming (including #1547). For 6) we have added a flag to move some compiler time errors to runtime.

@tmikov tmikov added need more info Awating additional info before proceeding and removed bug Something isn't working labels Dec 14, 2024
@gibson042
Copy link

I think endojs/endo#2655 and tc39/test262#4340 provide good summaries:

w.r.t. function caller and arguments properties…

  • JSC and SM follow spec by defining them only on Function.prototype (but violate it by having distinct values for the 4 respective getter/setter functions).
  • Hermes violates the spec by omitting them from Function.prototype, and additionally by defining them on strict function(){…} instances (non-configurable accessor properties with %ThrowTypeError% getter/setter functions).

In short, caller and arguments accessor properties should be defined on Function.prototype and not directly on function instances, and those properties should be configurable so that user code can remove them.

@avp
Copy link
Contributor

avp commented Dec 17, 2024

For caller and arguments in strict mode: I'm pretty sure our implementation was referencing the 5.1 spec, we never realized that this changed in ES6, and it hasn't really mattered because most of our code is strict mode and nobody uses these.

The ES5.1 spec on function creation (step 19 b,c) says that "caller" and "arguments" in strict mode are defined on the function itself and are non-configurable.

EDIT: As noted above, this is fixed for strict mode in Static Hermes (static_h branch).

@tmikov
Copy link
Contributor

tmikov commented Dec 17, 2024

@gibson042 As @avp mentioned, this has already been fixed in the Static Hermes branch. That is the branch being developed and supported.

But I am curious - why do .caller and arguments actually matter? Since they do nothing but throw, what is the use case? Is this a theoretical "doesn't 100% follow the spec" thing?

@gibson042
Copy link

The biggest problem is their non-configurability... Endo tries to ensure uniformity, and not being able to delete these properties interferes with that.

@tmikov tmikov added bug Something isn't working fixed-in-sh Fixed in SH but "wontfix" in Hermes and removed need more info Awating additional info before proceeding labels Dec 18, 2024
@tmikov
Copy link
Contributor

tmikov commented Dec 18, 2024

Fixed in bb90b33.

I am applying the label fixed-in-sh, meaning that this has been fixed in Static Hermes but is "Wontfix" in Hermes. We need this label because technically SH has not been released yet, but at the same time we don't want to keep issues like this open.

@tmikov tmikov closed this as completed Dec 18, 2024
@gibson042
Copy link

@tmikov Thanks!

@leotm
Copy link
Author

leotm commented Dec 18, 2024

thanks ^^ tested the change our side here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed-in-sh Fixed in SH but "wontfix" in Hermes
Projects
None yet
Development

No branches or pull requests

4 participants