Skip to content

Commit

Permalink
n-api: implement date object
Browse files Browse the repository at this point in the history
Implements `napi_create_date()` as well as `napi_is_date()` to
allow working with JavaScript Date objects.

Backport-PR-URL: nodejs#28298
PR-URL: nodejs#25917
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
jarrodconnolly authored and BethGriggs committed Sep 3, 2019
1 parent 55692ba commit 4f41e4f
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 1 deletion.
73 changes: 73 additions & 0 deletions doc/api/n-api.md
Expand Up @@ -184,6 +184,7 @@ typedef enum {
napi_queue_full,
napi_closing,
napi_bigint_expected,
napi_date_expected,
} napi_status;
```
If additional information is required upon an API returning a failed status,
Expand Down Expand Up @@ -1468,6 +1469,31 @@ This API allocates a `node::Buffer` object and initializes it with data copied
from the passed-in buffer. While this is still a fully-supported data
structure, in most cases using a `TypedArray` will suffice.
#### napi_create_date
<!-- YAML
added: REPLACEME
napiVersion: 4
-->
> Stability: 1 - Experimental
```C
napi_status napi_create_date(napi_env env,
double time,
napi_value* result);
```

- `[in] env`: The environment that the API is invoked under.
- `[in] time`: ECMAScript time value in milliseconds since 01 January, 1970 UTC.
- `[out] result`: A `napi_value` representing a JavaScript `Date`.

Returns `napi_ok` if the API succeeded.

This API allocates a JavaScript `Date` object.

JavaScript `Date` objects are described in
[Section 20.3][] of the ECMAScript Language Specification.

#### napi_create_external
<!-- YAML
added: v8.0.0
Expand Down Expand Up @@ -2088,6 +2114,31 @@ Returns `napi_ok` if the API succeeded.
This API returns various properties of a `DataView`.
#### napi_get_date_value
<!-- YAML
added: REPLACEME
napiVersion: 4
-->
> Stability: 1 - Experimental
```C
napi_status napi_get_date_value(napi_env env,
napi_value value,
double* result)
```

- `[in] env`: The environment that the API is invoked under.
- `[in] value`: `napi_value` representing a JavaScript `Date`.
- `[out] result`: Time value as a `double` represented as milliseconds
since midnight at the beginning of 01 January, 1970 UTC.

Returns `napi_ok` if the API succeeded. If a non-date `napi_value` is passed
in it returns `napi_date_expected`.

This API returns the C double primitive of time value for the given JavaScript
`Date`.

#### napi_get_value_bool
<!-- YAML
added: v8.0.0
Expand Down Expand Up @@ -2672,6 +2723,27 @@ Returns `napi_ok` if the API succeeded.
This API checks if the `Object` passed in is a buffer.
### napi_is_date
<!-- YAML
added: REPLACEME
napiVersion: 4
-->
> Stability: 1 - Experimental
```C
napi_status napi_is_date(napi_env env, napi_value value, bool* result)
```

- `[in] env`: The environment that the API is invoked under.
- `[in] value`: The JavaScript value to check.
- `[out] result`: Whether the given `napi_value` represents a JavaScript `Date`
object.

Returns `napi_ok` if the API succeeded.

This API checks if the `Object` passed in is a date.

### napi_is_error
<!-- YAML
added: v8.0.0
Expand Down Expand Up @@ -4653,6 +4725,7 @@ This API may only be called from the main thread.
[Object Lifetime Management]: #n_api_object_lifetime_management
[Object Wrap]: #n_api_object_wrap
[Section 12.5.5]: https://tc39.github.io/ecma262/#sec-typeof-operator
[Section 20.3]: https://tc39.github.io/ecma262/#sec-date-objects
[Section 22.1]: https://tc39.github.io/ecma262/#sec-array-objects
[Section 22.2]: https://tc39.github.io/ecma262/#sec-typedarray-objects
[Section 24.1]: https://tc39.github.io/ecma262/#sec-arraybuffer-objects
Expand Down
45 changes: 44 additions & 1 deletion src/node_api.cc
Expand Up @@ -1376,6 +1376,7 @@ const char* error_messages[] = {nullptr,
"Thread-safe function queue is full",
"Thread-safe function handle is closing",
"A bigint was expected",
"A date was expected",
};

static inline napi_status napi_clear_last_error(napi_env env) {
Expand Down Expand Up @@ -1407,7 +1408,7 @@ napi_status napi_get_last_error_info(napi_env env,
// We don't have a napi_status_last as this would result in an ABI
// change each time a message was added.
static_assert(
node::arraysize(error_messages) == napi_bigint_expected + 1,
node::arraysize(error_messages) == napi_date_expected + 1,
"Count of error messages must match count of error values");
CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch);

Expand Down Expand Up @@ -4085,6 +4086,48 @@ napi_status napi_is_promise(napi_env env,
return napi_clear_last_error(env);
}

napi_status napi_create_date(napi_env env,
double time,
napi_value* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);

v8::MaybeLocal<v8::Value> maybe_date = v8::Date::New(env->context(), time);
CHECK_MAYBE_EMPTY(env, maybe_date, napi_generic_failure);

*result = v8impl::JsValueFromV8LocalValue(maybe_date.ToLocalChecked());

return GET_RETURN_STATUS(env);
}

napi_status napi_is_date(napi_env env,
napi_value value,
bool* is_date) {
CHECK_ENV(env);
CHECK_ARG(env, value);
CHECK_ARG(env, is_date);

*is_date = v8impl::V8LocalValueFromJsValue(value)->IsDate();

return napi_clear_last_error(env);
}

napi_status napi_get_date_value(napi_env env,
napi_value value,
double* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, value);
CHECK_ARG(env, result);

v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
RETURN_STATUS_IF_FALSE(env, val->IsDate(), napi_date_expected);

v8::Local<v8::Date> date = val.As<v8::Date>();
*result = date->ValueOf();

return GET_RETURN_STATUS(env);
}

napi_status napi_run_script(napi_env env,
napi_value script,
napi_value* result) {
Expand Down
14 changes: 14 additions & 0 deletions src/node_api.h
Expand Up @@ -676,6 +676,20 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func);

#ifdef NAPI_EXPERIMENTAL

// Dates
NAPI_EXTERN napi_status napi_create_date(napi_env env,
double time,
napi_value* result);

NAPI_EXTERN napi_status napi_is_date(napi_env env,
napi_value value,
bool* is_date);

NAPI_EXTERN napi_status napi_get_date_value(napi_env env,
napi_value value,
double* result);

// BigInt
NAPI_EXTERN napi_status napi_create_bigint_int64(napi_env env,
int64_t value,
napi_value* result);
Expand Down
1 change: 1 addition & 0 deletions src/node_api_types.h
Expand Up @@ -82,6 +82,7 @@ typedef enum {
napi_queue_full,
napi_closing,
napi_bigint_expected,
napi_date_expected,
} napi_status;

#if NAPI_VERSION >= 4
Expand Down
10 changes: 10 additions & 0 deletions test/addons-napi/test_date/binding.gyp
@@ -0,0 +1,10 @@
{
"targets": [
{
"target_name": "test_date",
"sources": [
"test_date.c"
]
}
]
}
21 changes: 21 additions & 0 deletions test/addons-napi/test_date/test.js
@@ -0,0 +1,21 @@
'use strict';

const common = require('../../common');

// This tests the date-related n-api calls

const assert = require('assert');
const test_date = require(`./build/${common.buildType}/test_date`);

const dateTypeTestDate = test_date.createDate(1549183351);
assert.strictEqual(test_date.isDate(dateTypeTestDate), true);

assert.strictEqual(test_date.isDate(new Date(1549183351)), true);

assert.strictEqual(test_date.isDate(2.4), false);
assert.strictEqual(test_date.isDate('not a date'), false);
assert.strictEqual(test_date.isDate(undefined), false);
assert.strictEqual(test_date.isDate(null), false);
assert.strictEqual(test_date.isDate({}), false);

assert.strictEqual(test_date.getDateValue(new Date(1549183351)), 1549183351);
67 changes: 67 additions & 0 deletions test/addons-napi/test_date/test_date.c
@@ -0,0 +1,67 @@
#define NAPI_EXPERIMENTAL

#include <node_api.h>
#include "../common.h"

static napi_value createDate(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");

napi_valuetype valuetype0;
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

NAPI_ASSERT(env, valuetype0 == napi_number,
"Wrong type of arguments. Expects a number as first argument.");

double time;
NAPI_CALL(env, napi_get_value_double(env, args[0], &time));

napi_value date;
NAPI_CALL(env, napi_create_date(env, time, &date));

return date;
}

static napi_value isDate(napi_env env, napi_callback_info info) {
napi_value date, result;
size_t argc = 1;
bool is_date;

NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &date, NULL, NULL));
NAPI_CALL(env, napi_is_date(env, date, &is_date));
NAPI_CALL(env, napi_get_boolean(env, is_date, &result));

return result;
}

static napi_value getDateValue(napi_env env, napi_callback_info info) {
napi_value date, result;
size_t argc = 1;
double value;

NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &date, NULL, NULL));
NAPI_CALL(env, napi_get_date_value(env, date, &value));
NAPI_CALL(env, napi_create_double(env, value, &result));

return result;
}

EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NAPI_PROPERTY("createDate", createDate),
DECLARE_NAPI_PROPERTY("isDate", isDate),
DECLARE_NAPI_PROPERTY("getDateValue", getDateValue),
};

NAPI_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));

return exports;
}
EXTERN_C_END

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

0 comments on commit 4f41e4f

Please sign in to comment.