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

worker: make MessagePort constructor non-callable #28032

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,14 @@ non-writable `stdout` or `stderr` stream.

A constructor for a class was called without `new`.

<a id="ERR_CONSTRUCT_CALL_INVALID"></a>
### ERR_CONSTRUCT_CALL_INVALID
<!--
added: REPLACEME
-->

A class constructor was called that is not callable.

<a id="ERR_CPU_USAGE"></a>
### ERR_CPU_USAGE

Expand Down
4 changes: 3 additions & 1 deletion src/node_errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ void FatalException(v8::Isolate* isolate,
V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, Error) \
V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \
V(ERR_BUFFER_TOO_LARGE, Error) \
V(ERR_CONSTRUCT_CALL_REQUIRED, Error) \
V(ERR_CONSTRUCT_CALL_REQUIRED, TypeError) \
V(ERR_CONSTRUCT_CALL_INVALID, TypeError) \
V(ERR_INVALID_ARG_VALUE, TypeError) \
V(ERR_INVALID_ARG_TYPE, TypeError) \
V(ERR_INVALID_MODULE_SPECIFIER, TypeError) \
Expand Down Expand Up @@ -99,6 +100,7 @@ void FatalException(v8::Isolate* isolate,
#define PREDEFINED_ERROR_MESSAGES(V) \
V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, \
"Buffer is not available for the current Context") \
V(ERR_CONSTRUCT_CALL_INVALID, "Constructor cannot be called") \
V(ERR_CONSTRUCT_CALL_REQUIRED, "Cannot call constructor without `new`") \
V(ERR_INVALID_TRANSFER_OBJECT, "Found invalid object in transferList") \
V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") \
Expand Down
32 changes: 12 additions & 20 deletions src/node_messaging.cc
Original file line number Diff line number Diff line change
Expand Up @@ -529,33 +529,26 @@ void MessagePort::Close(v8::Local<v8::Value> close_callback) {
}

void MessagePort::New(const FunctionCallbackInfo<Value>& args) {
// This constructor just throws an error. Unfortunately, we can’t use V8’s
// ConstructorBehavior::kThrow, as that also removes the prototype from the
// class (i.e. makes it behave like an arrow function).
Environment* env = Environment::GetCurrent(args);
if (!args.IsConstructCall()) {
THROW_ERR_CONSTRUCT_CALL_REQUIRED(env);
return;
}

Local<Context> context = args.This()->CreationContext();
Context::Scope context_scope(context);

new MessagePort(env, context, args.This());
THROW_ERR_CONSTRUCT_CALL_INVALID(env);
}

MessagePort* MessagePort::New(
Environment* env,
Local<Context> context,
std::unique_ptr<MessagePortData> data) {
Context::Scope context_scope(context);
Local<Function> ctor;
if (!GetMessagePortConstructor(env, context).ToLocal(&ctor))
return nullptr;
Local<FunctionTemplate> ctor_templ = GetMessagePortConstructorTemplate(env);

// Construct a new instance, then assign the listener instance and possibly
// the MessagePortData to it.
Local<Object> instance;
if (!ctor->NewInstance(context).ToLocal(&instance))
if (!ctor_templ->InstanceTemplate()->NewInstance(context).ToLocal(&instance))
return nullptr;
MessagePort* port = Unwrap<MessagePort>(instance);
MessagePort* port = new MessagePort(env, context, instance);
CHECK_NOT_NULL(port);
if (data) {
port->Detach();
Expand Down Expand Up @@ -830,13 +823,12 @@ void MessagePort::Entangle(MessagePort* a, MessagePortData* b) {
MessagePortData::Entangle(a->data_.get(), b);
}

MaybeLocal<Function> GetMessagePortConstructor(
Environment* env, Local<Context> context) {
Local<FunctionTemplate> GetMessagePortConstructorTemplate(Environment* env) {
// Factor generating the MessagePort JS constructor into its own piece
// of code, because it is needed early on in the child environment setup.
Local<FunctionTemplate> templ = env->message_port_constructor_template();
if (!templ.IsEmpty())
return templ->GetFunction(context);
return templ;

Isolate* isolate = env->isolate();

Expand All @@ -859,7 +851,7 @@ MaybeLocal<Function> GetMessagePortConstructor(
env->set_message_event_object_template(e);
}

return GetMessagePortConstructor(env, context);
return GetMessagePortConstructorTemplate(env);
}

namespace {
Expand Down Expand Up @@ -902,8 +894,8 @@ static void InitMessaging(Local<Object> target,

target->Set(context,
env->message_port_constructor_string(),
GetMessagePortConstructor(env, context).ToLocalChecked())
.Check();
GetMessagePortConstructorTemplate(env)
->GetFunction(context).ToLocalChecked()).Check();
Copy link
Member

@joyeecheung joyeecheung Jun 5, 2019

Choose a reason for hiding this comment

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

Can't we just use ConstructorBehavior::kThrow for this? If there is a particular reason for throwing our own style of errors I think that's worth a comment..

Copy link
Member Author

Choose a reason for hiding this comment

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

@joyeecheung Yeah, it doesn’t work because that also removes the prototype property. I’m adding a comment, though.


// These are not methods on the MessagePort prototype, because
// the browser equivalents do not provide them.
Expand Down
4 changes: 2 additions & 2 deletions src/node_messaging.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ class MessagePort : public HandleWrap {
friend class MessagePortData;
};

v8::MaybeLocal<v8::Function> GetMessagePortConstructor(
Environment* env, v8::Local<v8::Context> context);
v8::Local<v8::FunctionTemplate> GetMessagePortConstructorTemplate(
Environment* env);

} // namespace worker
} // namespace node
Expand Down
27 changes: 27 additions & 0 deletions test/parallel/test-worker-message-port-constructor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';
require('../common');
const assert = require('assert');

const { MessageChannel, MessagePort } = require('worker_threads');

// Make sure that `MessagePort` is the constructor for MessagePort instances,
// but not callable.
const { port1 } = new MessageChannel();

assert(port1 instanceof MessagePort);
assert.strictEqual(port1.constructor, MessagePort);

assert.throws(() => MessagePort(), {
constructor: TypeError,
code: 'ERR_CONSTRUCT_CALL_INVALID'
});

assert.throws(() => new MessagePort(), {
constructor: TypeError,
code: 'ERR_CONSTRUCT_CALL_INVALID'
});

assert.throws(() => MessageChannel(), {
constructor: TypeError,
code: 'ERR_CONSTRUCT_CALL_REQUIRED'
});
7 changes: 0 additions & 7 deletions test/parallel/test-worker-message-port-move.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,6 @@ vm.runInContext('(' + function() {
}
assert(threw);
}

{
const newDummyPort = new (port.constructor)();
assert(!(newDummyPort instanceof MessagePort));
assert(newDummyPort.close instanceof Function);
newDummyPort.close();
}
} + ')()', context);

port2.on('message', common.mustCall((msg) => {
Expand Down