-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
process: introduce codeGenerationFromString event #48295
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
The 'codeGenerationFromString' event is emitted when a call is made to `eval` or `new Function`. Co-authored-by: Thomas Watson <w@tson.dk>
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ typedef int mode_t; | |
|
|
||
| namespace node { | ||
|
|
||
| using errors::TryCatchScope; | ||
| using v8::Array; | ||
| using v8::ArrayBuffer; | ||
| using v8::CFunction; | ||
|
|
@@ -463,6 +464,57 @@ static void ReallyExit(const FunctionCallbackInfo<Value>& args) { | |
| env->Exit(code); | ||
| } | ||
|
|
||
| static void CodeGenerationFromStringsAllowed( | ||
| const FunctionCallbackInfo<Value>& args) { | ||
| Environment* env = Environment::GetCurrent(args); | ||
| Local<Context> context = env->context(); | ||
| bool value = context->IsCodeGenerationFromStringsAllowed(); | ||
| args.GetReturnValue().Set(value); | ||
| } | ||
|
|
||
| static v8::ModifyCodeGenerationFromStringsResult CodeGenCallback( | ||
| Local<Context> context, | ||
| Local<Value> source, | ||
| bool is_code_like) { | ||
| Environment* env = Environment::GetCurrent(context); | ||
| TryCatchScope try_catch(env); | ||
|
|
||
| ProcessEmit(env, "codeGenerationFromString", source); | ||
|
|
||
| // V8 does not expect this callback to have a scheduled exceptions once it | ||
| // returns, so we print them out in a best effort to do something about it | ||
| // without failing silently and without crashing the process. | ||
| if (try_catch.HasCaught() && !try_catch.HasTerminated()) { | ||
| Isolate* isolate = env->isolate(); | ||
| fprintf(stderr, "Exception in codeGenerationFromString event callback:\n"); | ||
| PrintCaughtException(isolate, env->context(), try_catch); | ||
| } | ||
|
|
||
| // returning {true, val} where val.IsEmpty() makes v8 | ||
| // use the orignal value passed to `eval` which does not impact | ||
| // calls as `eval({})` | ||
| return {true, v8::MaybeLocal<v8::String>()}; | ||
| } | ||
|
|
||
| static void SetEmitCodeGenFromStringEvent( | ||
| const FunctionCallbackInfo<Value>& args) { | ||
| Environment* env = Environment::GetCurrent(args); | ||
| Isolate* isolate = args.GetIsolate(); | ||
| Local<Context> context = env->context(); | ||
|
|
||
| CHECK(args[0]->IsBoolean()); | ||
|
|
||
| bool val = args[0]->BooleanValue(args.GetIsolate()); | ||
| if (val) { | ||
| context->AllowCodeGenerationFromStrings(false); | ||
| isolate->SetModifyCodeGenerationFromStringsCallback(CodeGenCallback); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would replace the I also noticed that
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the callback can be installed when needed (either with the cli flag |
||
| } else { | ||
| // This is enough to disable the handler. V8 will not call it anymore | ||
| // until set back to false | ||
| context->AllowCodeGenerationFromStrings(true); | ||
| } | ||
| } | ||
|
|
||
| namespace process { | ||
|
|
||
| BindingData::BindingData(Realm* realm, v8::Local<v8::Object> object) | ||
|
|
@@ -600,6 +652,10 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data, | |
| SetMethod(isolate, target, "reallyExit", ReallyExit); | ||
| SetMethodNoSideEffect(isolate, target, "uptime", Uptime); | ||
| SetMethod(isolate, target, "patchProcessObject", PatchProcessObject); | ||
| SetMethod(isolate, target, "codeGenerationFromStringsAllowed", | ||
| CodeGenerationFromStringsAllowed); | ||
| SetMethod(isolate, target, "setEmitCodeGenFromStringEvent", | ||
| SetEmitCodeGenFromStringEvent); | ||
| } | ||
|
|
||
| static void CreatePerContextProperties(Local<Object> target, | ||
|
|
@@ -637,6 +693,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { | |
| registry->Register(ReallyExit); | ||
| registry->Register(Uptime); | ||
| registry->Register(PatchProcessObject); | ||
| registry->Register(CodeGenerationFromStringsAllowed); | ||
| registry->Register(SetEmitCodeGenFromStringEvent); | ||
| } | ||
|
|
||
| } // namespace process | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| 'use strict'; | ||
|
|
||
| const common = require('../common'); | ||
| const assert = require('assert'); | ||
|
|
||
| const item = { foo: 0 }; | ||
| eval('++item.foo'); | ||
|
|
||
| assert.strictEqual(item.foo, 1); | ||
|
|
||
| process.on('codeGenerationFromString', common.mustCall(handleError((code) => { | ||
| assert.strictEqual(code, 'item.foo++'); | ||
| }))); | ||
|
|
||
| eval('item.foo++'); | ||
| assert.strictEqual(item.foo, 2); | ||
|
|
||
| process.removeAllListeners('codeGenerationFromString'); | ||
|
|
||
| process.on('codeGenerationFromString', common.mustCall(handleError((code) => { | ||
| assert.strictEqual(code, '(function anonymous(a,b\n) {\nreturn a + b\n})'); | ||
| }))); | ||
|
|
||
| const fct = new Function('a', 'b', 'return a + b'); | ||
| assert.strictEqual(fct(1, 2), 3); | ||
|
|
||
| function handleError(fn) { | ||
| return (...args) => { | ||
| try { | ||
| fn(...args); | ||
| } catch (err) { | ||
| // The C++ code will just log the error to stderr and continue with the | ||
| // flow of the program. Set the exit code manually to ensure the test | ||
| // script fails in case of an error. | ||
| process.exitCode = 1; | ||
| throw err; | ||
| } | ||
| }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.