buffer: throw exception when creating from non-Node.js Context
Throw an exception instead of crashing when attempting to create `Buffer` objects from a Context that is not associated with a Node.js `Environment`. Possible alternatives for the future might be just returning a plain `Uint8Array`, or working on providing `Buffer` for all `Context`s. PR-URL: https://github.com/nodejs/node/pull/23938 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
This commit is contained in:
parent
d8fb81fab3
commit
9c7d19104d
@ -613,6 +613,19 @@ An attempt was made to register something that is not a function as an
|
|||||||
The type of an asynchronous resource was invalid. Note that users are also able
|
The type of an asynchronous resource was invalid. Note that users are also able
|
||||||
to define their own types if using the public embedder API.
|
to define their own types if using the public embedder API.
|
||||||
|
|
||||||
|
<a id="ERR_BUFFER_CONTEXT_NOT_AVAILABLE"></a>
|
||||||
|
### ERR_BUFFER_CONTEXT_NOT_AVAILABLE
|
||||||
|
|
||||||
|
An attempt was made to create a Node.js `Buffer` instance from addon or embedder
|
||||||
|
code, while in a JS engine Context that is not associated with a Node.js
|
||||||
|
instance. The data passed to the `Buffer` method will have been released
|
||||||
|
by the time the method returns.
|
||||||
|
|
||||||
|
When encountering this error, a possible alternative to creating a `Buffer`
|
||||||
|
instance is to create a normal `Uint8Array`, which only differs in the
|
||||||
|
prototype of the resulting object. `Uint8Array`s are generally accepted in all
|
||||||
|
Node.js core APIs where `Buffer`s are; they are available in all Contexts.
|
||||||
|
|
||||||
<a id="ERR_BUFFER_OUT_OF_BOUNDS"></a>
|
<a id="ERR_BUFFER_OUT_OF_BOUNDS"></a>
|
||||||
### ERR_BUFFER_OUT_OF_BOUNDS
|
### ERR_BUFFER_OUT_OF_BOUNDS
|
||||||
|
|
||||||
|
@ -271,7 +271,10 @@ MaybeLocal<Object> New(Isolate* isolate, size_t length) {
|
|||||||
EscapableHandleScope handle_scope(isolate);
|
EscapableHandleScope handle_scope(isolate);
|
||||||
Local<Object> obj;
|
Local<Object> obj;
|
||||||
Environment* env = Environment::GetCurrent(isolate);
|
Environment* env = Environment::GetCurrent(isolate);
|
||||||
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here.
|
if (env == nullptr) {
|
||||||
|
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate);
|
||||||
|
return MaybeLocal<Object>();
|
||||||
|
}
|
||||||
if (Buffer::New(env, length).ToLocal(&obj))
|
if (Buffer::New(env, length).ToLocal(&obj))
|
||||||
return handle_scope.Escape(obj);
|
return handle_scope.Escape(obj);
|
||||||
return Local<Object>();
|
return Local<Object>();
|
||||||
@ -314,7 +317,10 @@ MaybeLocal<Object> New(Environment* env, size_t length) {
|
|||||||
MaybeLocal<Object> Copy(Isolate* isolate, const char* data, size_t length) {
|
MaybeLocal<Object> Copy(Isolate* isolate, const char* data, size_t length) {
|
||||||
EscapableHandleScope handle_scope(isolate);
|
EscapableHandleScope handle_scope(isolate);
|
||||||
Environment* env = Environment::GetCurrent(isolate);
|
Environment* env = Environment::GetCurrent(isolate);
|
||||||
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here.
|
if (env == nullptr) {
|
||||||
|
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate);
|
||||||
|
return MaybeLocal<Object>();
|
||||||
|
}
|
||||||
Local<Object> obj;
|
Local<Object> obj;
|
||||||
if (Buffer::Copy(env, data, length).ToLocal(&obj))
|
if (Buffer::Copy(env, data, length).ToLocal(&obj))
|
||||||
return handle_scope.Escape(obj);
|
return handle_scope.Escape(obj);
|
||||||
@ -364,7 +370,11 @@ MaybeLocal<Object> New(Isolate* isolate,
|
|||||||
void* hint) {
|
void* hint) {
|
||||||
EscapableHandleScope handle_scope(isolate);
|
EscapableHandleScope handle_scope(isolate);
|
||||||
Environment* env = Environment::GetCurrent(isolate);
|
Environment* env = Environment::GetCurrent(isolate);
|
||||||
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here.
|
if (env == nullptr) {
|
||||||
|
callback(data, hint);
|
||||||
|
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate);
|
||||||
|
return MaybeLocal<Object>();
|
||||||
|
}
|
||||||
Local<Object> obj;
|
Local<Object> obj;
|
||||||
if (Buffer::New(env, data, length, callback, hint).ToLocal(&obj))
|
if (Buffer::New(env, data, length, callback, hint).ToLocal(&obj))
|
||||||
return handle_scope.Escape(obj);
|
return handle_scope.Escape(obj);
|
||||||
@ -403,7 +413,11 @@ MaybeLocal<Object> New(Environment* env,
|
|||||||
MaybeLocal<Object> New(Isolate* isolate, char* data, size_t length) {
|
MaybeLocal<Object> New(Isolate* isolate, char* data, size_t length) {
|
||||||
EscapableHandleScope handle_scope(isolate);
|
EscapableHandleScope handle_scope(isolate);
|
||||||
Environment* env = Environment::GetCurrent(isolate);
|
Environment* env = Environment::GetCurrent(isolate);
|
||||||
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here.
|
if (env == nullptr) {
|
||||||
|
free(data);
|
||||||
|
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate);
|
||||||
|
return MaybeLocal<Object>();
|
||||||
|
}
|
||||||
Local<Object> obj;
|
Local<Object> obj;
|
||||||
if (Buffer::New(env, data, length).ToLocal(&obj))
|
if (Buffer::New(env, data, length).ToLocal(&obj))
|
||||||
return handle_scope.Escape(obj);
|
return handle_scope.Escape(obj);
|
||||||
|
@ -21,6 +21,7 @@ namespace node {
|
|||||||
// a `Local<Value>` containing the TypeError with proper code and message
|
// a `Local<Value>` containing the TypeError with proper code and message
|
||||||
|
|
||||||
#define ERRORS_WITH_CODE(V) \
|
#define ERRORS_WITH_CODE(V) \
|
||||||
|
V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, Error) \
|
||||||
V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \
|
V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \
|
||||||
V(ERR_BUFFER_TOO_LARGE, Error) \
|
V(ERR_BUFFER_TOO_LARGE, Error) \
|
||||||
V(ERR_CANNOT_TRANSFER_OBJECT, TypeError) \
|
V(ERR_CANNOT_TRANSFER_OBJECT, TypeError) \
|
||||||
@ -64,6 +65,8 @@ namespace node {
|
|||||||
// Errors with predefined static messages
|
// Errors with predefined static messages
|
||||||
|
|
||||||
#define PREDEFINED_ERROR_MESSAGES(V) \
|
#define PREDEFINED_ERROR_MESSAGES(V) \
|
||||||
|
V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, \
|
||||||
|
"Buffer is not available for the current Context") \
|
||||||
V(ERR_CANNOT_TRANSFER_OBJECT, "Cannot transfer object of unsupported type")\
|
V(ERR_CANNOT_TRANSFER_OBJECT, "Cannot transfer object of unsupported type")\
|
||||||
V(ERR_CLOSED_MESSAGE_PORT, "Cannot send data on closed MessagePort") \
|
V(ERR_CLOSED_MESSAGE_PORT, "Cannot send data on closed MessagePort") \
|
||||||
V(ERR_CONSTRUCT_CALL_REQUIRED, "Cannot call constructor without `new`") \
|
V(ERR_CONSTRUCT_CALL_REQUIRED, "Cannot call constructor without `new`") \
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <node.h>
|
#include <node.h>
|
||||||
|
#include <node_buffer.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -15,6 +16,17 @@ using v8::Script;
|
|||||||
using v8::String;
|
using v8::String;
|
||||||
using v8::Value;
|
using v8::Value;
|
||||||
|
|
||||||
|
inline void MakeBufferInNewContext(
|
||||||
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
|
Isolate* isolate = args.GetIsolate();
|
||||||
|
Local<Context> context = Context::New(isolate);
|
||||||
|
Context::Scope context_scope(context);
|
||||||
|
|
||||||
|
// This should throw an exception, rather than actually do anything.
|
||||||
|
MaybeLocal<Object> buf = node::Buffer::Copy(isolate, "foo", 3);
|
||||||
|
assert(buf.IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
inline void RunInNewContext(
|
inline void RunInNewContext(
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
Isolate* isolate = args.GetIsolate();
|
Isolate* isolate = args.GetIsolate();
|
||||||
@ -41,13 +53,8 @@ inline void RunInNewContext(
|
|||||||
inline void Initialize(Local<Object> exports,
|
inline void Initialize(Local<Object> exports,
|
||||||
Local<Value> module,
|
Local<Value> module,
|
||||||
Local<Context> context) {
|
Local<Context> context) {
|
||||||
Isolate* isolate = context->GetIsolate();
|
NODE_SET_METHOD(exports, "runInNewContext", RunInNewContext);
|
||||||
Local<String> key = String::NewFromUtf8(
|
NODE_SET_METHOD(exports, "makeBufferInNewContext", MakeBufferInNewContext);
|
||||||
isolate, "runInNewContext", NewStringType::kNormal).ToLocalChecked();
|
|
||||||
Local<Function> value = FunctionTemplate::New(isolate, RunInNewContext)
|
|
||||||
->GetFunction(context)
|
|
||||||
.ToLocalChecked();
|
|
||||||
assert(exports->Set(context, key, value).IsJust());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
22
test/addons/non-node-context/test-make-buffer.js
Normal file
22
test/addons/non-node-context/test-make-buffer.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const {
|
||||||
|
makeBufferInNewContext
|
||||||
|
} = require(`./build/${common.buildType}/binding`);
|
||||||
|
|
||||||
|
// Because the `Buffer` function and its protoype property only (currently)
|
||||||
|
// exist in a Node.js instance’s main context, trying to create buffers from
|
||||||
|
// another context throws an exception.
|
||||||
|
|
||||||
|
try {
|
||||||
|
makeBufferInNewContext();
|
||||||
|
} catch (exception) {
|
||||||
|
assert.strictEqual(exception.constructor.name, 'Error');
|
||||||
|
assert(!(exception.constructor instanceof Error));
|
||||||
|
|
||||||
|
assert.strictEqual(exception.code, 'ERR_BUFFER_CONTEXT_NOT_AVAILABLE');
|
||||||
|
assert.strictEqual(exception.message,
|
||||||
|
'Buffer is not available for the current Context');
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user