Skip to content

Commit

Permalink
feat(endomoat): support native modules in policy gen
Browse files Browse the repository at this point in the history
  • Loading branch information
boneskull committed Mar 6, 2024
1 parent 67ad9ac commit 585e144
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 9 deletions.
1 change: 1 addition & 0 deletions packages/endomoat/package.json
Expand Up @@ -37,6 +37,7 @@
"scripts": {
"lint:deps": "depcheck",
"test": "npm run test:run",
"test:prep": "cd test/fixture/native && npm install",
"test:run": "ava"
},
"dependencies": {
Expand Down
4 changes: 4 additions & 0 deletions packages/endomoat/src/constants.js
Expand Up @@ -29,3 +29,7 @@ export const RSRC_POLICY_GLOBALS = 'globals'

export const LMR_TYPE_BUILTIN = 'builtin'
export const LMR_TYPE_SOURCE = 'js'

export const LMR_TYPE_NATIVE = 'native'

export const ENDO_PARSER_BYTES = 'bytes'
30 changes: 21 additions & 9 deletions packages/endomoat/src/policy-gen/policy-generator-context.js
Expand Up @@ -8,8 +8,10 @@ import { isBuiltin as nodeIsBuiltin } from 'node:module'
import { fileURLToPath } from 'node:url'
import path from 'path'
import {
ENDO_PARSER_BYTES,
LAVAMOAT_PKG_POLICY_ROOT,
LMR_TYPE_BUILTIN,
LMR_TYPE_NATIVE,
LMR_TYPE_SOURCE,
} from '../constants.js'
import { defaultReadPower } from '../power.js'
Expand Down Expand Up @@ -271,7 +273,10 @@ export class PolicyGeneratorContext {
* @returns {Promise<import('lavamoat-core').LavamoatModuleRecord[]>}
* @internal
*/
async buildModuleRecordsForSource(specifier, { record, sourceLocation }) {
async buildModuleRecordsForSource(
specifier,
{ parser, record, sourceLocation }
) {
if (!sourceLocation) {
// XXX: why would we not have a sourceLocation?
throw new TypeError(
Expand Down Expand Up @@ -299,17 +304,24 @@ export class PolicyGeneratorContext {
* The `ModuleSource.content` prop is already pre-processed by Endo, and we
* do not want that, since it befouls our AST crawling.
*
* This will not be run if the `parser` is `bytes`.
*
* @remarks
* Doing this first since it may be more likely to fail than the other
* operations below
* @type {NonNullable<
* import('lavamoat-core').LavamoatModuleRecord['content']
* >}
* operations below.
* @type {string | undefined}
* @todo Modify Endo to surface the original source
*
* @todo Add more exceptions to the parsers?
*/
const content = await this.readPower(sourceLocation).then((buffer) =>
PolicyGeneratorContext.#decoder.decode(buffer)
)
let content

await Promise.resolve()
if (parser !== ENDO_PARSER_BYTES) {
content = await this.readPower(sourceLocation).then((buffer) =>
PolicyGeneratorContext.#decoder.decode(buffer)
)
}

/**
* The {@link LavamoatModuleRecord.file} prop
Expand Down Expand Up @@ -338,7 +350,7 @@ export class PolicyGeneratorContext {
packageName: this.packageName,
importMap,
content,
type: LMR_TYPE_SOURCE,
type: parser === ENDO_PARSER_BYTES ? LMR_TYPE_NATIVE : LMR_TYPE_SOURCE,
}),
]

Expand Down
2 changes: 2 additions & 0 deletions packages/endomoat/test/fixture/native/.npmrc
@@ -0,0 +1,2 @@
# needed to build native module
ignore-scripts=false
2 changes: 2 additions & 0 deletions packages/endomoat/test/fixture/native/app.js
@@ -0,0 +1,2 @@
import addon from 'napi'
export const hello = addon.hello()
@@ -0,0 +1,5 @@
This is an example of a module using Node-API (a.k.a. N-API) to provide a native module.

It was modified from the [nodejs/node-addon-examples](https://github.com/nodejs/node-addon-examples/tree/f8561c0233c3a75a99fab5ec20e729ae1fd0a1f6/src/1-getting-started/1_hello_world/napi) repo.

The original uses the [bindings](https://npm.im/bindings) package, but it was removed here since that package uses dynamic requires.
@@ -0,0 +1,8 @@
{
"targets": [
{
"target_name": "hello",
"sources": [ "hello.c" ]
}
]
}
23 changes: 23 additions & 0 deletions packages/endomoat/test/fixture/native/node-modules/napi/hello.c
@@ -0,0 +1,23 @@
#include <assert.h>
#include <node_api.h>

static napi_value Method(napi_env env, napi_callback_info info) {
napi_status status;
napi_value world;
status = napi_create_string_utf8(env, "world", 5, &world);
assert(status == napi_ok);
return world;
}

#define DECLARE_NAPI_METHOD(name, func) \
{ name, 0, func, 0, 0, 0, napi_default, 0 }

static napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method);
status = napi_define_properties(env, exports, 1, &desc);
assert(status == napi_ok);
return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
@@ -0,0 +1,5 @@
var addon = require('./build/Release/hello')

console.log(addon.hello()) // 'world'

module.exports = addon
@@ -0,0 +1,14 @@
{
"name": "napi",
"version": "0.0.0",
"description": "Node.js Addons Example #1",
"private": true,
"main": "hello.js",
"scripts": {
"test": "node hello.js"
},
"gypfile": true,
"parsers": {
"node": "bytes"
}
}
24 changes: 24 additions & 0 deletions packages/endomoat/test/fixture/native/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions packages/endomoat/test/fixture/native/package.json
@@ -0,0 +1,11 @@
{
"name": "no-deps",
"version": "1.0.0",
"type": "module",
"license": "ISC",
"private": true,
"main": "app.js",
"dependencies": {
"napi": "file://./node-modules/napi"
}
}

0 comments on commit 585e144

Please sign in to comment.