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

vm: add code generation options #19016

Closed
wants to merge 1 commit into from

Conversation

devsnek
Copy link
Member

@devsnek devsnek commented Feb 26, 2018

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

vm, src

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. labels Feb 26, 2018
@devsnek devsnek force-pushed the feature/vm-context-allowcgfs branch from 35f2f7c to ade9c38 Compare February 26, 2018 20:46
@@ -184,6 +184,32 @@ Local<Context> ContextifyContext::CreateV8Context(
CHECK(name->IsString());
Utf8Value name_val(env->isolate(), name);

Local<Value> codegen = options_obj->Get(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "codeGeneration"))
.ToLocalChecked();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use 4 spaces indentation for these lines as well, like other statement continuations?

.ToLocalChecked();
if (!allowwcg->IsUndefined()) {
CHECK(allowwcg->IsBoolean());
if(allowwcg.As<Boolean>()->Value() == false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nit: space after if

if (!allowwcg->IsUndefined()) {
CHECK(allowwcg->IsBoolean());
if(allowwcg.As<Boolean>()->Value() == false)
ctx->SetEmbedderData(kAllowWasmCodeGeneration, True(env->isolate()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I missing something? Shouldn’t this be true? (Or you could probably set the field directly to allowwcg)

{
const ctx = createContext({}, { codeGeneration: {
strings: false,
} });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nit: Can you make codeGeneration have its own three lines?


const ctx = createContext({ bytes }, { codeGeneration: {
wasm: false,
} });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

src/node.cc Outdated
CHECK(wcg->IsBoolean());
return wcg.As<Boolean>()->Value();
}
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be just return wcg->IsTrue(); (also below.)

CHECK(allowwcg->IsBoolean());
if(allowwcg.As<Boolean>()->Value() == false)
ctx->SetEmbedderData(kAllowWasmCodeGeneration, True(env->isolate()));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apropos the variable names, allow_wasm_code_generation is clearer than allowwcg.

CHECK(codegen->IsObject());
Local<Object> codegen_obj = codegen.As<Object>();

Local<Value> allowcgfs = codegen_obj->Get(env->context(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming: allow_code_generation_from_strings

ctx->AllowCodeGenerationFromStrings(allowcgfs.As<Boolean>()->Value());
}

Local<Value> allowwcg = codegen_obj->Get(env->context(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise: allow_wasm_code_generation

src/node.cc Outdated
@@ -4417,6 +4418,16 @@ inline int Start(uv_loop_t* event_loop,
isolate->SetAbortOnUncaughtExceptionCallback(ShouldAbortOnUncaughtException);
isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
isolate->SetFatalErrorHandler(OnFatalError);
isolate->SetAllowWasmCodeGenerationCallback(
[](Local<Context> context, Local<String>) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this a static member of ContextifyContext?

@devsnek devsnek force-pushed the feature/vm-context-allowcgfs branch from ade9c38 to afa8677 Compare February 26, 2018 22:46
@devsnek
Copy link
Member Author

devsnek commented Feb 26, 2018

@devsnek devsnek force-pushed the feature/vm-context-allowcgfs branch from 83d67bf to b52b2e9 Compare February 26, 2018 23:42
@TimothyGu TimothyGu added vm Issues and PRs related to the vm subsystem. and removed lib / src Issues and PRs related to general changes in the lib or src directory. labels Feb 27, 2018
@devsnek
Copy link
Member Author

devsnek commented Feb 27, 2018

CI failures unrelated

@bnoordhuis comments addressed

CHECK(wcg->IsBoolean());
return wcg.As<Boolean>()->Value();
}
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just return wcg->IsUndefined() || wcg->IsTrue();?

Aside: wasm_code_generation, or wasm_code_gen if you think that's too verbose, but no abbrevs.

if (!allow_code_generation_from_strings->IsUndefined()) {
CHECK(allow_code_generation_from_strings->IsBoolean());
ctx->AllowCodeGenerationFromStrings(
allow_code_generation_from_strings.As<Boolean>()->Value());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise. You don't even need the if.

.ToLocalChecked();
if (!allow_wasm_code_generation->IsUndefined()) {
CHECK(allow_wasm_code_generation->IsBoolean());
ctx->SetEmbedderData(kAllowWasmCodeGeneration,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better set it unconditionally because calling GetEmbedderData() on an undefined index is undefined behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better idea seems to be setting the slot to false unconditionally in NewContext() and then override the value in contextity, since the wasm callback is called on the main context as well.

Copy link
Member Author

@devsnek devsnek Feb 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'll do tgu's approach

sidenote @nodejs/v8 is there any reason why we don't have Context::AllowWasmCodeGeneration/can it be added? it seems like the current approach here cause issues for people making native modules and creating a new context under node's isolate? what if someone takes over the callback without realizing that node uses it, etc.

@devsnek devsnek force-pushed the feature/vm-context-allowcgfs branch from b52b2e9 to f765a8f Compare February 28, 2018 01:35
fn();
assert.fail('expected fn to error');
} catch (err) {
console.log(err);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the console.log() is an intended part of the test, can you add a comment. Otherwise, please remove it.

@@ -0,0 +1,70 @@
'use strict';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also needs tests for unexpected types of contextCodeGeneration and its properties.

@devsnek devsnek force-pushed the feature/vm-context-allowcgfs branch 2 times, most recently from b9f0737 to c616b4a Compare February 28, 2018 15:36
lib/vm.js Outdated
origin: options.contextOrigin,
codeGeneration:
validateObject(options.contextCodeGeneration,
'options.contextCodeGeneration') ? {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic doesn’t currently work when options.contextCodeGeneration is undefined.

@devsnek devsnek force-pushed the feature/vm-context-allowcgfs branch from c616b4a to 7327c16 Compare February 28, 2018 16:01
lib/vm.js Outdated
function getContextOptions(options) {
if (options) {
const contextOptions = {
name: options.contextName,
origin: options.contextOrigin
origin: options.contextOrigin,
codeGeneration: options.contextCodeGeneration ? {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The typeof options.contextCodeGeneration === 'object' check should be kept though.

@devsnek devsnek force-pushed the feature/vm-context-allowcgfs branch from 7327c16 to 83d5ebd Compare March 1, 2018 01:28
@devsnek
Copy link
Member Author

devsnek commented Mar 1, 2018

lib/vm.js Outdated
}

function validateObject(prop, propName) {
if (prop !== undefined && prop !== null && typeof prop !== 'object')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for keeping going back and forth on this, but we don’t want to allow null here right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea that seems right. I think I accidentally inverted the standard object type check in my head which is why the null is there, I'll fix it

@devsnek devsnek force-pushed the feature/vm-context-allowcgfs branch from 83d5ebd to cbccf6c Compare March 1, 2018 15:10
lib/vm.js Outdated
function validateObject(prop, propName) {
if (prop !== undefined && (prop === null || typeof prop !== 'object'))
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', propName,
'object', prop);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be 'Object'. I would also rather combine these validation functions instead of having one for each type.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how would i combine them

@devsnek devsnek force-pushed the feature/vm-context-allowcgfs branch from cbccf6c to 486ead3 Compare March 2, 2018 15:37
@devsnek devsnek requested a review from TimothyGu March 4, 2018 01:52
TimothyGu
TimothyGu previously approved these changes Mar 4, 2018
lib/vm.js Outdated
function validate(type, prop, propName) {
if (prop !== undefined &&
((type === 'Object' && prop === null) ||
typeof prop !== type.toLowerCase()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is overly complicated, and would disallow V8 from inlining the typeof check. How it was (one validation function per type) was just fine.

src/node.cc Outdated
@@ -4442,6 +4445,8 @@ inline int Start(uv_loop_t* event_loop,
isolate->SetAbortOnUncaughtExceptionCallback(ShouldAbortOnUncaughtException);
isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
isolate->SetFatalErrorHandler(OnFatalError);
isolate->SetAllowWasmCodeGenerationCallback(
contextify::ContextifyContext::AllowWasmCodeGeneration);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Node.js core shouldn't have to depend on contextify. Please move AllowWasmCodeGeneration to node.cc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TimothyGu I think this was done by an explicit suggestion from @bnoordhuis … is there any specific reason not to do it this way?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conceptually, AllowWasmCodeGeneration is used for all contexts, not just contexts created by contextify. It feels natural to move this function (which itself doesn't use any contextify-specific machinery) to node.cc.

Sorry for going back and forth on this, I didn't realize @bnoordhuis suggested this way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i agree with timothy, but i'm going to wait for my other context pr to get merged before i work on this more

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, when I made that suggestion, it was only setting it for ContextifyContext objects. It makes sense to move it back now.

src/node.cc Outdated
@@ -4347,6 +4348,8 @@ Local<Context> NewContext(Isolate* isolate,
HandleScope handle_scope(isolate);
auto intl_key = FIXED_ONE_BYTE_STRING(isolate, "Intl");
auto break_iter_key = FIXED_ONE_BYTE_STRING(isolate, "v8BreakIterator");
context->SetEmbedderData(
contextify::ContextifyContext::kAllowWasmCodeGeneration, True(isolate));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: move this to before intl_key declaration.

Environment* const env_;
Persistent<v8::Context> context_;

public:
// V8 reserves the first field in context objects for the debugger. We use the
// second field to hold a reference to the sandbox object.
enum { kSandboxObjectIndex = 1, kAllowWasmCodeGeneration = 2 };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the indices this way would make non-VM contexts have a hole at index 1, which doesn't sound too desirable. Also, ditto on the "core should not depend on contextify" comment here.

In other words:

  1. Move kAllowWasmCodeGeneration to node_internals.h, and set it to 1. Keep kSandboxObjectIndex here however.
  2. Make kSandboxObjectIndex equal to kAllowWasmCodeGeneration + 1.

You don't have to fix it now (though you certainly could), but "V8 reserves the first field in context objects for the debugger" is no longer true since v8/v8@5f90a6eb067. This means that kAllowWasmCodeGeneration could just be 0.

@TimothyGu TimothyGu dismissed their stale review March 4, 2018 19:24

Oops, meant to click the "comment" button rather than "approve."

@devsnek devsnek requested review from BridgeAR and TimothyGu March 9, 2018 01:03
@fhinkel
Copy link
Member

fhinkel commented Mar 9, 2018

Can you please add a few paragraph to the commit message that explain this change. Thanks!

.ToLocalChecked();

if (!codegen->IsUndefined()) {
CHECK(codegen->IsObject());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could leave a comment here, that the js part already ensured that we have undefined or an object. Looking only at the C++ part, my first response was that this CHECK would fail a lot.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this pattern it used a lot with stuff that is validated js-side and we don't leave usually comments, are you worried about something specific not being construed?

throw new ERR_INVALID_ARG_TYPE(propName, 'boolean', prop);
}

function validateObject(prop, propName) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we rather name this function validateObjectOrUndefined?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

none of the others are named like that, and it seems fairly self-explanatory

Adds options to a VM Context to disable code generation from strings
(such as eval or new Function) and WASM code generation
(WebAssembly.compile).
@devsnek devsnek force-pushed the feature/vm-context-allowcgfs branch from 43b83d8 to ecc0e20 Compare March 9, 2018 18:41
@devsnek devsnek added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 12, 2018
@devsnek
Copy link
Member Author

devsnek commented Mar 12, 2018

landed in cb5f358

@devsnek devsnek closed this Mar 12, 2018
@devsnek devsnek deleted the feature/vm-context-allowcgfs branch March 12, 2018 19:39
devsnek added a commit that referenced this pull request Mar 12, 2018
Adds options to a VM Context to disable code generation from strings
(such as eval or new Function) and WASM code generation
(WebAssembly.compile).

PR-URL: #19016
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
@jdalton
Copy link
Member

jdalton commented Mar 13, 2018

Is there a feature request for this or a usage scenario?

@devsnek
Copy link
Member Author

devsnek commented Mar 13, 2018

@jdalton i just have a general agenda of adding as much goodness to vm as possible; i really like vm :D

i also am using this in an eval bot i have in a discord support guild for javascript

@targos targos added semver-minor PRs that contain new features and should be released in the next minor version. and removed author ready PRs that have at least one approval, no pending requests for changes, and a CI started. labels Mar 17, 2018
@targos
Copy link
Member

targos commented Mar 17, 2018

This is semver-minor, right?

@targos
Copy link
Member

targos commented Mar 17, 2018

Should this be backported to v9.x-staging? If yes please follow the guide and raise a backport PR, if not let me know or add the dont-land-on label.

@devsnek
Copy link
Member Author

devsnek commented Mar 17, 2018

looks like 9.x doesn't have v8::Isolate::SetAllowWasmCodeGenerationCallback, maybe it can be cherry picked in?

TomCoded added a commit to TomCoded/node that referenced this pull request Mar 19, 2018
Add changes entries for vm.createContext codeGeneration
option and script.runInNewContext contextCodeGeneration
option.

fixes: nodejs#19419
refs: nodejs#19016
@gibfahn
Copy link
Member

gibfahn commented Mar 19, 2018

Should be backported with #19440

tniessen pushed a commit that referenced this pull request Mar 25, 2018
Add changes entries for vm.createContext codeGeneration
option and script.runInNewContext contextCodeGeneration
option.

PR-URL: #19440
Fixes: #19419
Refs: #19016
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Adds options to a VM Context to disable code generation from strings
(such as eval or new Function) and WASM code generation
(WebAssembly.compile).

PR-URL: nodejs#19016
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. semver-minor PRs that contain new features and should be released in the next minor version. vm Issues and PRs related to the vm subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.