• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23const common = require('../common');
24const assert = require('assert');
25const vm = require('vm');
26
27// vm.runInNewContext
28{
29  const sandbox = {};
30  const result = vm.runInNewContext(
31    'foo = "bar"; this.typeofProcess = typeof process; typeof Object;',
32    sandbox
33  );
34  assert.deepStrictEqual(sandbox, {
35    foo: 'bar',
36    typeofProcess: 'undefined',
37  });
38  assert.strictEqual(result, 'function');
39}
40
41// vm.runInContext
42{
43  const sandbox = { foo: 'bar' };
44  const context = vm.createContext(sandbox);
45  const result = vm.runInContext(
46    'baz = foo; this.typeofProcess = typeof process; typeof Object;',
47    context
48  );
49  assert.deepStrictEqual(sandbox, {
50    foo: 'bar',
51    baz: 'bar',
52    typeofProcess: 'undefined'
53  });
54  assert.strictEqual(result, 'function');
55}
56
57// vm.runInThisContext
58{
59  const result = vm.runInThisContext(
60    'vmResult = "foo"; Object.prototype.toString.call(process);'
61  );
62  assert.strictEqual(global.vmResult, 'foo');
63  assert.strictEqual(result, '[object process]');
64  delete global.vmResult;
65}
66
67// vm.runInNewContext
68{
69  const result = vm.runInNewContext(
70    'vmResult = "foo"; typeof process;'
71  );
72  assert.strictEqual(global.vmResult, undefined);
73  assert.strictEqual(result, 'undefined');
74}
75
76// vm.createContext
77{
78  const sandbox = {};
79  const context = vm.createContext(sandbox);
80  assert.strictEqual(sandbox, context);
81}
82
83// Run script with filename
84{
85  const script = 'throw new Error("boom")';
86  const filename = 'test-boom-error';
87  const context = vm.createContext();
88
89  function checkErr(err) {
90    return err.stack.startsWith('test-boom-error:1');
91  }
92
93  assert.throws(() => vm.runInContext(script, context, filename), checkErr);
94  assert.throws(() => vm.runInNewContext(script, context, filename), checkErr);
95  assert.throws(() => vm.runInThisContext(script, filename), checkErr);
96}
97
98// Invalid arguments
99[null, 'string'].forEach((input) => {
100  assert.throws(() => {
101    vm.createContext({}, input);
102  }, {
103    code: 'ERR_INVALID_ARG_TYPE',
104    name: 'TypeError',
105    message: 'The "options" argument must be of type object.' +
106             common.invalidArgTypeHelper(input)
107  });
108});
109
110['name', 'origin'].forEach((propertyName) => {
111  assert.throws(() => {
112    vm.createContext({}, { [propertyName]: null });
113  }, {
114    code: 'ERR_INVALID_ARG_TYPE',
115    name: 'TypeError',
116    message: `The "options.${propertyName}" property must be of type string. ` +
117             'Received null'
118  });
119});
120
121['contextName', 'contextOrigin'].forEach((propertyName) => {
122  assert.throws(() => {
123    vm.runInNewContext('', {}, { [propertyName]: null });
124  }, {
125    code: 'ERR_INVALID_ARG_TYPE',
126    name: 'TypeError',
127    message: `The "options.${propertyName}" property must be of type string. ` +
128             'Received null'
129  });
130});
131
132// vm.compileFunction
133{
134  assert.strictEqual(
135    vm.compileFunction('console.log("Hello, World!")').toString(),
136    'function () {\nconsole.log("Hello, World!")\n}'
137  );
138
139  assert.strictEqual(
140    vm.compileFunction(
141      'return p + q + r + s + t',
142      ['p', 'q', 'r', 's', 't']
143    )('ab', 'cd', 'ef', 'gh', 'ij'),
144    'abcdefghij'
145  );
146
147  vm.compileFunction('return'); // Should not throw on 'return'
148
149  assert.throws(() => {
150    vm.compileFunction(
151      '});\n\n(function() {\nconsole.log(1);\n})();\n\n(function() {'
152    );
153  }, {
154    name: 'SyntaxError',
155    message: "Unexpected token '}'"
156  });
157
158  // Tests for failed argument validation
159  assert.throws(() => vm.compileFunction(), {
160    name: 'TypeError',
161    code: 'ERR_INVALID_ARG_TYPE',
162    message: 'The "code" argument must be of type string. ' +
163      'Received undefined'
164  });
165
166  vm.compileFunction(''); // Should pass without params or options
167
168  assert.throws(() => vm.compileFunction('', null), {
169    name: 'TypeError',
170    code: 'ERR_INVALID_ARG_TYPE',
171    message: 'The "params" argument must be an instance of Array. ' +
172      'Received null'
173  });
174
175  // vm.compileFunction('', undefined, null);
176
177  const optionTypes = {
178    'filename': 'string',
179    'columnOffset': 'number',
180    'lineOffset': 'number',
181    'cachedData': 'Buffer, TypedArray, or DataView',
182    'produceCachedData': 'boolean',
183  };
184
185  for (const option in optionTypes) {
186    const typeErrorMessage = `The "options.${option}" property must be ` +
187      (option === 'cachedData' ? 'an instance of' : 'of type');
188    assert.throws(() => {
189      vm.compileFunction('', undefined, { [option]: null });
190    }, {
191      name: 'TypeError',
192      code: 'ERR_INVALID_ARG_TYPE',
193      message: typeErrorMessage +
194        ` ${optionTypes[option]}. Received null`
195    });
196  }
197
198  // Testing for context-based failures
199  [Boolean(), Number(), null, String(), Symbol(), {}].forEach(
200    (value) => {
201      assert.throws(() => {
202        vm.compileFunction('', undefined, { parsingContext: value });
203      }, {
204        name: 'TypeError',
205        code: 'ERR_INVALID_ARG_TYPE',
206        message: 'The "options.parsingContext" property must be an instance ' +
207          `of Context.${common.invalidArgTypeHelper(value)}`
208      });
209    }
210  );
211
212  // Testing for non Array type-based failures
213  [Boolean(), Number(), null, Object(), Symbol(), {}].forEach(
214    (value) => {
215      assert.throws(() => {
216        vm.compileFunction('', value);
217      }, {
218        name: 'TypeError',
219        code: 'ERR_INVALID_ARG_TYPE',
220        message: 'The "params" argument must be an instance of Array.' +
221          common.invalidArgTypeHelper(value)
222      });
223    }
224  );
225
226  assert.strictEqual(
227    vm.compileFunction(
228      'return a;',
229      undefined,
230      { contextExtensions: [{ a: 5 }] }
231    )(),
232    5
233  );
234
235  assert.throws(() => {
236    vm.compileFunction('', undefined, { contextExtensions: null });
237  }, {
238    name: 'TypeError',
239    code: 'ERR_INVALID_ARG_TYPE',
240    message: 'The "options.contextExtensions" property must be an instance of' +
241       ' Array. Received null'
242  });
243
244  assert.throws(() => {
245    vm.compileFunction('', undefined, { contextExtensions: [0] });
246  }, {
247    name: 'TypeError',
248    code: 'ERR_INVALID_ARG_TYPE',
249    message: 'The "options.contextExtensions[0]" property must be of type ' +
250       'object. Received type number (0)'
251  });
252
253  const oldLimit = Error.stackTraceLimit;
254  // Setting value to run the last three tests
255  Error.stackTraceLimit = 1;
256
257  assert.throws(() => {
258    vm.compileFunction('throw new Error("Sample Error")')();
259  }, {
260    message: 'Sample Error',
261    stack: 'Error: Sample Error\n    at <anonymous>:1:7'
262  });
263
264  assert.throws(() => {
265    vm.compileFunction(
266      'throw new Error("Sample Error")',
267      [],
268      { lineOffset: 3 }
269    )();
270  }, {
271    message: 'Sample Error',
272    stack: 'Error: Sample Error\n    at <anonymous>:4:7'
273  });
274
275  assert.throws(() => {
276    vm.compileFunction(
277      'throw new Error("Sample Error")',
278      [],
279      { columnOffset: 3 }
280    )();
281  }, {
282    message: 'Sample Error',
283    stack: 'Error: Sample Error\n    at <anonymous>:1:10'
284  });
285
286  assert.strictEqual(
287    vm.compileFunction(
288      'return varInContext',
289      [],
290      {
291        parsingContext: vm.createContext({ varInContext: 'abc' })
292      }
293    )(),
294    'abc'
295  );
296
297  assert.throws(() => {
298    vm.compileFunction(
299      'return varInContext',
300      []
301    )();
302  }, {
303    message: 'varInContext is not defined',
304    stack: 'ReferenceError: varInContext is not defined\n    at <anonymous>:1:1'
305  });
306
307  assert.notDeepStrictEqual(
308    vm.compileFunction(
309      'return global',
310      [],
311      {
312        parsingContext: vm.createContext({ global: {} })
313      }
314    )(),
315    global
316  );
317
318  assert.deepStrictEqual(
319    vm.compileFunction(
320      'return global',
321      []
322    )(),
323    global
324  );
325
326  // Resetting value
327  Error.stackTraceLimit = oldLimit;
328}
329