<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @flow
import * as React from 'react';
import { Provider } from 'react-redux';
import { render } from '@testing-library/react';

import { makeProfileSerializable } from '../../profile-logic/process-profile';
import { getView, getUrlSetupPhase } from '../../selectors/app';
import { UrlManager } from '../../components/app/UrlManager';
import { blankStore } from '../fixtures/stores';
import {
  getDataSource,
  getHash,
  getCurrentSearchString,
} from '../../selectors/url-state';
import { waitUntilState } from '../fixtures/utils';
import { createGeckoProfile } from '../fixtures/profiles/gecko-profile';
import { getProfileFromTextSamples } from '../fixtures/profiles/processed-profile';
import { CURRENT_URL_VERSION } from '../../app-logic/url-handling';
import { autoMockFullNavigation } from '../fixtures/mocks/window-navigation';
import { profilePublished } from 'firefox-profiler/actions/publish';
import {
  changeCallTreeSearchString,
  setDataSource,
} from 'firefox-profiler/actions/profile-view';

jest.mock('../../profile-logic/symbol-store');

describe('UrlManager', function() {
  autoMockFullNavigation();

  // This is a quite complicated function, that does something very simple:
  // it returns a response with a good profile suitable to mock a fetch result.
  function getSuccessfulFetchResponse() {
    const fetch200Response = (({
      ok: true,
      status: 200,
      headers: {
        get: () => 'application/json',
      },
      json: () =>
        Promise.resolve(
          makeProfileSerializable(getProfileFromTextSamples('A').profile)
        ),
    }: any): Response);
    return fetch200Response;
  }

  function setup(urlPath: ?string) {
    if (typeof urlPath === 'string') {
      window.location.replace(urlPath);
    }
    const store = blankStore();
    const { dispatch, getState } = store;

    const createUrlManager = () =>
      render(
        <Provider store={store}>
          <UrlManager>Contents</UrlManager>
        </Provider>
      );

    const waitUntilUrlSetupPhase = phase =>
      waitUntilState(store, state => getUrlSetupPhase(state) === phase);

    return { dispatch, getState, createUrlManager, waitUntilUrlSetupPhase };
  }

  beforeEach(function() {
    const profileJSON = createGeckoProfile();
    const mockGetProfile = jest.fn().mockResolvedValue(profileJSON);

    const geckoProfiler = {
      getProfile: mockGetProfile,
      getSymbolTable: jest
        .fn()
        .mockRejectedValue(new Error('No symbol tables available')),
    };
    window.fetch = jest
      .fn()
      .mockRejectedValue(new Error('Simulated network error'));
    window.geckoProfilerPromise = Promise.resolve(geckoProfiler);
  });

  afterEach(function() {
    delete window.geckoProfilerPromise;
    delete window.fetch;
  });

  it('sets up the URL', async function() {
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup();

    expect(getUrlSetupPhase(getState())).toBe('initial-load');
    createUrlManager();

    expect(getUrlSetupPhase(getState())).toBe('loading-profile');

    await waitUntilUrlSetupPhase('done');
    expect(getUrlSetupPhase(getState())).toBe('done');
    expect(getDataSource(getState())).toMatch('none');
  });

  it('has no data source by default', async function() {
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup();
    createUrlManager();
    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('none');
  });

  it('sets the data source to from-addon', async function() {
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup(
      '/from-addon/'
    );
    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('from-addon');
  });

  it('redirects from-file back to no data source', async function() {
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup(
      '/from-file/'
    );
    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('none');
  });

  it(`sets the data source to public and doesn't change the URL when there's a fetch error`, async function() {
    const urlPath = '/public/FAKE_HASH/marker-chart';
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup(
      urlPath
    );
    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('public');
    const view: any = getView(getState());
    expect(view.phase).toBe('FATAL_ERROR');
    expect(view.error).toBeTruthy();
    expect(view.error.message).toBe('Simulated network error');
    expect(window.location.pathname).toBe(urlPath);
  });

  it(`sets the data source to public and doesn't change the URL when there's a URL upgrading error`, async function() {
    window.fetch.mockResolvedValue(getSuccessfulFetchResponse());

    const urlPath = '/public/FAKE_HASH/calltree';
    const searchString = '?v=' + (CURRENT_URL_VERSION + 1);
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup(
      urlPath + searchString
    );
    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('public');
    const view: any = getView(getState());
    expect(view.phase).toBe('FATAL_ERROR');
    expect(view.error).toBeTruthy();
    expect(view.error.message).toMatch('Unable to parse a url');
    expect(view.error.name).toBe('UrlUpgradeError');
    expect(window.location.pathname).toBe(urlPath);
    expect(window.location.search).toBe(searchString);
  });

  it(`fetches profile and sets the phase to done when everything works`, async function() {
    window.fetch.mockResolvedValue(getSuccessfulFetchResponse());

    const urlPath = '/public/FAKE_HASH/';
    const expectedResultingPath = urlPath + 'calltree/';
    const searchString = 'v=' + CURRENT_URL_VERSION;

    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup(
      urlPath + '?' + searchString
    );

    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('public');
    expect(getView(getState()).phase).toBe('DATA_LOADED');
    expect(window.location.pathname).toBe(expectedResultingPath);
    expect(window.location.search).toContain(searchString);
  });

  it('allows navigating back and forward when changing view options', async () => {
    window.fetch.mockResolvedValue(getSuccessfulFetchResponse());

    const urlPath = '/public/FAKE_HASH/calltree/';
    const searchString = 'v=' + CURRENT_URL_VERSION;

    const {
      getState,
      createUrlManager,
      waitUntilUrlSetupPhase,
      dispatch,
    } = setup(urlPath + '?' + searchString);

    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');

    expect(window.history.length).toBe(1);

    // The user changes is looking for some specific call node.
    dispatch(changeCallTreeSearchString('B'));
    expect(getCurrentSearchString(getState())).toBe('B');
    expect(window.history.length).toBe(2);

    // The user can't find anything, he goes back in history.
    window.history.back();
    expect(getCurrentSearchString(getState())).toBe('');
    expect(window.history.length).toBe(2);

    // Look again at this search.
    window.history.forward();
    expect(getCurrentSearchString(getState())).toBe('B');
  });

  it('allows navigating back and forward when moving between content pages', async () => {
    // The test will start at the home.
    const urlPath = '/ ';
    const {
      getState,
      createUrlManager,
      waitUntilUrlSetupPhase,
      dispatch,
    } = setup(urlPath);
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toBe('none');
    expect(window.history.length).toBe(1);

    // Now the user clicks on the "all recordings" link. This will change the
    // datasource, we're simulating that.
    dispatch(setDataSource('uploaded-recordings'));
    expect(getDataSource(getState())).toBe('uploaded-recordings');
    expect(window.history.length).toBe(2);

    // The user goes back to the home by pressing the browser's back button.
    window.history.back();
    expect(getDataSource(getState())).toBe('none');

    // Now the user goes to the compare form clicking a link on the homepage,
    // we're simulating that by changing the data source.
    dispatch(setDataSource('compare'));
    expect(getDataSource(getState())).toBe('compare');
    // Click on the header
    dispatch(setDataSource('none'));
    expect(window.history.length).toBe(3);

    // The user goes back to the compare form using the browser's back button.
    window.history.back();
    expect(getDataSource(getState())).toBe('compare');
    // The user goes back to the home using the browser's back button.
    window.history.back();
    expect(getDataSource(getState())).toBe('none');
  });

  it('prevents navigating back after publishing', async () => {
    // This loads a profile using the add-on.
    const {
      getState,
      dispatch,
      createUrlManager,
      waitUntilUrlSetupPhase,
    } = setup('/from-addon/');
    createUrlManager();
    await waitUntilUrlSetupPhase('done');

    expect(window.history.length).toBe(1);

    // Now the user publishes.
    dispatch(profilePublished('SOME_HASH', null));
    expect(getDataSource(getState())).toMatch('public');
    expect(getHash(getState())).toMatch('SOME_HASH');
    expect(window.history.length).toBe(2);

    // Then wants to go back in history. This shouldn't work!
    let previousLocation = window.location.href;
    window.history.back();
    expect(getDataSource(getState())).toMatch('public');
    expect(getHash(getState())).toMatch('SOME_HASH');
    expect(previousLocation).toEqual(window.location.href);

    // We went back, the entry number 2 has been replaced, but there are still 3
    // entries in the history.
    expect(window.history.length).toBe(2);

    // Now let's publish again
    dispatch(profilePublished('SOME_OTHER_HASH', null));
    expect(getDataSource(getState())).toMatch('public');
    expect(getHash(getState())).toMatch('SOME_OTHER_HASH');

    // It's still 3 because the 3rd entry has been removed and replaced by this
    // new state (remember we were at entry number 2).
    expect(window.history.length).toBe(2);

    // The user wants to go back, but this won't work!
    previousLocation = window.location.href;
    window.history.back();
    expect(getDataSource(getState())).toMatch('public');
    expect(getHash(getState())).toMatch('SOME_OTHER_HASH');
    expect(previousLocation).toEqual(window.location.href);
  });
});
<commit_msg>Add a test for the persistence of view query string on from-addon<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @flow
import * as React from 'react';
import { Provider } from 'react-redux';
import { render } from '@testing-library/react';

import { makeProfileSerializable } from '../../profile-logic/process-profile';
import { getView, getUrlSetupPhase } from '../../selectors/app';
import { UrlManager } from '../../components/app/UrlManager';
import { blankStore } from '../fixtures/stores';
import {
  getDataSource,
  getHash,
  getCurrentSearchString,
  getTimelineTrackOrganization,
} from '../../selectors/url-state';
import { waitUntilState } from '../fixtures/utils';
import { createGeckoProfile } from '../fixtures/profiles/gecko-profile';
import { getProfileFromTextSamples } from '../fixtures/profiles/processed-profile';
import { CURRENT_URL_VERSION } from '../../app-logic/url-handling';
import { autoMockFullNavigation } from '../fixtures/mocks/window-navigation';
import { profilePublished } from 'firefox-profiler/actions/publish';
import {
  changeCallTreeSearchString,
  setDataSource,
} from 'firefox-profiler/actions/profile-view';

jest.mock('../../profile-logic/symbol-store');

describe('UrlManager', function() {
  autoMockFullNavigation();

  // This is a quite complicated function, that does something very simple:
  // it returns a response with a good profile suitable to mock a fetch result.
  function getSuccessfulFetchResponse() {
    const fetch200Response = (({
      ok: true,
      status: 200,
      headers: {
        get: () => 'application/json',
      },
      json: () =>
        Promise.resolve(
          makeProfileSerializable(getProfileFromTextSamples('A').profile)
        ),
    }: any): Response);
    return fetch200Response;
  }

  function setup(urlPath: ?string) {
    if (typeof urlPath === 'string') {
      window.location.replace(urlPath);
    }
    const store = blankStore();
    const { dispatch, getState } = store;

    const createUrlManager = () =>
      render(
        <Provider store={store}>
          <UrlManager>Contents</UrlManager>
        </Provider>
      );

    const waitUntilUrlSetupPhase = phase =>
      waitUntilState(store, state => getUrlSetupPhase(state) === phase);

    return { dispatch, getState, createUrlManager, waitUntilUrlSetupPhase };
  }

  beforeEach(function() {
    const profileJSON = createGeckoProfile();
    const mockGetProfile = jest.fn().mockResolvedValue(profileJSON);

    const geckoProfiler = {
      getProfile: mockGetProfile,
      getSymbolTable: jest
        .fn()
        .mockRejectedValue(new Error('No symbol tables available')),
    };
    window.fetch = jest
      .fn()
      .mockRejectedValue(new Error('Simulated network error'));
    window.geckoProfilerPromise = Promise.resolve(geckoProfiler);
  });

  afterEach(function() {
    delete window.geckoProfilerPromise;
    delete window.fetch;
  });

  it('sets up the URL', async function() {
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup();

    expect(getUrlSetupPhase(getState())).toBe('initial-load');
    createUrlManager();

    expect(getUrlSetupPhase(getState())).toBe('loading-profile');

    await waitUntilUrlSetupPhase('done');
    expect(getUrlSetupPhase(getState())).toBe('done');
    expect(getDataSource(getState())).toMatch('none');
  });

  it('has no data source by default', async function() {
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup();
    createUrlManager();
    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('none');
  });

  it('sets the data source to from-addon', async function() {
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup(
      '/from-addon/'
    );
    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('from-addon');
  });

  it('redirects from-file back to no data source', async function() {
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup(
      '/from-file/'
    );
    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('none');
  });

  it(`sets the data source to public and doesn't change the URL when there's a fetch error`, async function() {
    const urlPath = '/public/FAKE_HASH/marker-chart';
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup(
      urlPath
    );
    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('public');
    const view: any = getView(getState());
    expect(view.phase).toBe('FATAL_ERROR');
    expect(view.error).toBeTruthy();
    expect(view.error.message).toBe('Simulated network error');
    expect(window.location.pathname).toBe(urlPath);
  });

  it(`sets the data source to public and doesn't change the URL when there's a URL upgrading error`, async function() {
    window.fetch.mockResolvedValue(getSuccessfulFetchResponse());

    const urlPath = '/public/FAKE_HASH/calltree';
    const searchString = '?v=' + (CURRENT_URL_VERSION + 1);
    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup(
      urlPath + searchString
    );
    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('public');
    const view: any = getView(getState());
    expect(view.phase).toBe('FATAL_ERROR');
    expect(view.error).toBeTruthy();
    expect(view.error.message).toMatch('Unable to parse a url');
    expect(view.error.name).toBe('UrlUpgradeError');
    expect(window.location.pathname).toBe(urlPath);
    expect(window.location.search).toBe(searchString);
  });

  it(`fetches profile and sets the phase to done when everything works`, async function() {
    window.fetch.mockResolvedValue(getSuccessfulFetchResponse());

    const urlPath = '/public/FAKE_HASH/';
    const expectedResultingPath = urlPath + 'calltree/';
    const searchString = 'v=' + CURRENT_URL_VERSION;

    const { getState, createUrlManager, waitUntilUrlSetupPhase } = setup(
      urlPath + '?' + searchString
    );

    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toMatch('public');
    expect(getView(getState()).phase).toBe('DATA_LOADED');
    expect(window.location.pathname).toBe(expectedResultingPath);
    expect(window.location.search).toContain(searchString);
  });

  it('allows navigating back and forward when changing view options', async () => {
    window.fetch.mockResolvedValue(getSuccessfulFetchResponse());

    const urlPath = '/public/FAKE_HASH/calltree/';
    const searchString = 'v=' + CURRENT_URL_VERSION;

    const {
      getState,
      createUrlManager,
      waitUntilUrlSetupPhase,
      dispatch,
    } = setup(urlPath + '?' + searchString);

    expect(getDataSource(getState())).toMatch('none');
    createUrlManager();

    await waitUntilUrlSetupPhase('done');

    expect(window.history.length).toBe(1);

    // The user changes is looking for some specific call node.
    dispatch(changeCallTreeSearchString('B'));
    expect(getCurrentSearchString(getState())).toBe('B');
    expect(window.history.length).toBe(2);

    // The user can't find anything, he goes back in history.
    window.history.back();
    expect(getCurrentSearchString(getState())).toBe('');
    expect(window.history.length).toBe(2);

    // Look again at this search.
    window.history.forward();
    expect(getCurrentSearchString(getState())).toBe('B');
  });

  it('allows navigating back and forward when moving between content pages', async () => {
    // The test will start at the home.
    const urlPath = '/ ';
    const {
      getState,
      createUrlManager,
      waitUntilUrlSetupPhase,
      dispatch,
    } = setup(urlPath);
    createUrlManager();

    await waitUntilUrlSetupPhase('done');
    expect(getDataSource(getState())).toBe('none');
    expect(window.history.length).toBe(1);

    // Now the user clicks on the "all recordings" link. This will change the
    // datasource, we're simulating that.
    dispatch(setDataSource('uploaded-recordings'));
    expect(getDataSource(getState())).toBe('uploaded-recordings');
    expect(window.history.length).toBe(2);

    // The user goes back to the home by pressing the browser's back button.
    window.history.back();
    expect(getDataSource(getState())).toBe('none');

    // Now the user goes to the compare form clicking a link on the homepage,
    // we're simulating that by changing the data source.
    dispatch(setDataSource('compare'));
    expect(getDataSource(getState())).toBe('compare');
    // Click on the header
    dispatch(setDataSource('none'));
    expect(window.history.length).toBe(3);

    // The user goes back to the compare form using the browser's back button.
    window.history.back();
    expect(getDataSource(getState())).toBe('compare');
    // The user goes back to the home using the browser's back button.
    window.history.back();
    expect(getDataSource(getState())).toBe('none');
  });

  it('prevents navigating back after publishing', async () => {
    // This loads a profile using the add-on.
    const {
      getState,
      dispatch,
      createUrlManager,
      waitUntilUrlSetupPhase,
    } = setup('/from-addon/');
    createUrlManager();
    await waitUntilUrlSetupPhase('done');

    expect(window.history.length).toBe(1);

    // Now the user publishes.
    dispatch(profilePublished('SOME_HASH', null));
    expect(getDataSource(getState())).toMatch('public');
    expect(getHash(getState())).toMatch('SOME_HASH');
    expect(window.history.length).toBe(2);

    // Then wants to go back in history. This shouldn't work!
    let previousLocation = window.location.href;
    window.history.back();
    expect(getDataSource(getState())).toMatch('public');
    expect(getHash(getState())).toMatch('SOME_HASH');
    expect(previousLocation).toEqual(window.location.href);

    // We went back, the entry number 2 has been replaced, but there are still 3
    // entries in the history.
    expect(window.history.length).toBe(2);

    // Now let's publish again
    dispatch(profilePublished('SOME_OTHER_HASH', null));
    expect(getDataSource(getState())).toMatch('public');
    expect(getHash(getState())).toMatch('SOME_OTHER_HASH');

    // It's still 3 because the 3rd entry has been removed and replaced by this
    // new state (remember we were at entry number 2).
    expect(window.history.length).toBe(2);

    // The user wants to go back, but this won't work!
    previousLocation = window.location.href;
    window.history.back();
    expect(getDataSource(getState())).toMatch('public');
    expect(getHash(getState())).toMatch('SOME_OTHER_HASH');
    expect(previousLocation).toEqual(window.location.href);
  });

  it('persists view query string for `from-addon` data source', async function() {
    const { getState, waitUntilUrlSetupPhase, createUrlManager } = setup(
      '/from-addon/?view=active-tab'
    );
    await createUrlManager();
    await waitUntilUrlSetupPhase('done');

    // It should successfully preserve the view query string and update the
    // timeline track organization state.
    expect(getTimelineTrackOrganization(getState()).type).toBe('active-tab');
  });
});
<|endoftext|>


<commit_before>'use strict';
var generators = require('yeoman-generator');
var path = require('path');
var _ = require('lodash');
var extend = _.merge;

module.exports = generators.Base.extend({
  constructor: function () {
    generators.Base.apply(this, arguments);

    this.option('generateInto', {
      type: String,
      required: false,
      defaults: '',
      desc: 'Relocate the location of the generated files.'
    });

    this.option('name', {
      type: String,
      required: true,
      desc: 'Project name'
    });

    this.option('styles', {
      type: Boolean,
      required: false,
      defaults: false,
      desc: 'Include CSS'
    });

    this.option('scss', {
      type: Boolean,
      required: false,
      defaults: true,
      desc: 'Use SCSS extension'
    });

    this.option('bower', {
      type: Boolean,
      required: false,
      defaults: true,
      desc: 'Include bower component'
    });
  },

  writing: {
    package: function () {
      var pkg = this.fs.readJSON(this.destinationPath(this.options.generateInto, 'package.json'), {});

      extend(pkg, {
        dependencies: {
          'lodash': '4.8.2'
        },
        devDependencies: {
          'browserify': '13.0.0',
          'browserify-istanbul': '2.0.0',
          'del': '2.0.2',
          'gulp': '3.9.1',
          'gulp-concat': '2.6.0',
          'gulp-help': '1.6.0',
          'gulp-inject': '4.0.0',
          'gulp-jasmine': '2.0.1',
          'gulp-jshint': '2.0.0',
          'gulp-load-plugins': '1.0.0-rc.1',
          'gulp-notify': '2.2.0',
          'gulp-rename': '1.2.2',
          'gulp-size': '2.1.0',
          'gulp-sourcemaps': '1.6.0',
          'gulp-streamify': '1.0.2',
          'gulp-tsc': '1.1.5',
          'gulp-tsconfig-files': '0.0.2',
          'gulp-tslint': '3.1',
          'gulp-typedoc': '1.2.1',
          'gulp-typescript': '2.12.2',
          'gulp-typings': '1.3.4',
          'gulp-uglify': '1.2.0',
          'gulp-util': '3.0.7',
          'jasmine': '2.3.1',
          'jasmine-ajax': '3.2.0',
          'jasmine-core': '2.4.1',
          'jshint': '2.9.1',
          'jshint-stylish': '2.1.0',
          'karma': '0.13.22',
          'karma-browserify': '5.0.3',
          'karma-chrome-launcher': '0.2.2',
          'karma-coverage': '0.5.5',
          'karma-jasmine': '0.3.7',
          'karma-phantomjs-launcher': '1.0.0',
          'karma-remap-istanbul': '0.0.5',
          'path': '0.12.7',
          'phantomjs-prebuilt': '2.1.7',
          'remap-istanbul': '0.5.1',
          'require-dir': '0.3.0',
          'run-sequence': '1.1.5',
          'tsify': '0.14.1',
          'typescript': '1.8.7',
          'vinyl-source-stream': '1.1.0',
          'watchify': '3.7.0'
        },
        scripts: {
          'typings': 'gulp typings-install',
          'test': 'gulp test',
          'dev test': 'gulp dev-test',
          'watch': 'gulp watch',
          'clean': 'gulp clean-build',
          'build': 'gulp build',
          'coverage': 'gulp coverage',
          'doc': 'gulp typedoc'
        }
      });

      if (this.options.styles) {
        pkg.devDependencies['gulp-cssmin'] = '0.1.7';
      }

      if (this.options.styles && this.options.scss) {
        pkg.devDependencies['gulp-sass'] = '2.2.0';
        pkg.devDependencies['gulp-scss-lint'] = '0.3.9';
      }

      if (this.options.styles && !this.options.scss) {
        pkg.devDependencies['gulp-csslint'] = '0.3.0';
      }

      this.fs.writeJSON(this.destinationPath(this.options.generateInto, 'package.json'), pkg);
    },

    gulpfile: function () {
      this.fs.copy(
        this.templatePath('gulpfile.js'),
        this.destinationPath(this.options.generateInto, 'gulpfile.js')
      );

      this.fs.copy(
        this.templatePath('build.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'build.js')
      );

      this.fs.copyTpl(
        this.templatePath('clean.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'clean.js'),
        {
          bower: this.options.bower,
          styles: this.options.styles
        }
      );

      this.fs.copyTpl(
        this.templatePath('conf.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'conf.js'),
        {
          projectName: this.options.name,
          bower: this.options.bower,
          styles: this.options.styles,
          scss: this.options.scss
        }
      );

      if (this.options.styles) {
        this.fs.copyTpl(
          this.templatePath('copy.js'),
          this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'copy.js'),
          {
            bower: this.options.bower
          }
        );
      }

      this.fs.copyTpl(
        this.templatePath('inject.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'inject.js'),
        {
          bower: this.options.bower,
          styles: this.options.styles
        }
      );

      if (this.options.styles && this.options.scss) {
        this.fs.copy(
          this.templatePath('sass.js'),
          this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'sass.js')
        );
      }

      if (this.options.styles && !this.options.scss) {
        this.fs.copy(
          this.templatePath('css.js'),
          this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'css.js')
        );
      }

      this.fs.copyTpl(
        this.templatePath('scripts.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'scripts.js'),
        {
          bower: this.options.bower,
          styles: this.options.styles,
          scss: this.options.scss
        }
      );

      this.fs.copy(
        this.templatePath('tests.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'tests.js')
      );

      this.fs.copy(
        this.templatePath('tsconfig.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'tsconfig.js')
      );

      this.fs.copy(
        this.templatePath('tsdocs.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'tsdocs.js')
      );

      this.fs.copyTpl(
        this.templatePath('watch.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'watch.js'),
        {
          styles: this.options.styles,
          scss: this.options.scss
        }
      );
    }
  }
});
<commit_msg>Updated index.js in gulp folder.<commit_after>'use strict';
var generators = require('yeoman-generator');
var path = require('path');
var _ = require('lodash');
var extend = _.merge;

module.exports = generators.Base.extend({
  constructor: function () {
    generators.Base.apply(this, arguments);

    this.option('generateInto', {
      type: String,
      required: false,
      defaults: '',
      desc: 'Relocate the location of the generated files.'
    });

    this.option('name', {
      type: String,
      required: true,
      desc: 'Project name'
    });

    this.option('styles', {
      type: Boolean,
      required: false,
      defaults: false,
      desc: 'Include CSS'
    });

    this.option('scss', {
      type: Boolean,
      required: false,
      defaults: true,
      desc: 'Use SCSS extension'
    });

    this.option('bower', {
      type: Boolean,
      required: false,
      defaults: true,
      desc: 'Include bower component'
    });
  },

  writing: {
    package: function () {
      var pkg = this.fs.readJSON(this.destinationPath(this.options.generateInto, 'package.json'), {});

      extend(pkg, {
        dependencies: {
          'lodash': '4.8.2'
        },
        devDependencies: {
          'browserify': '13.0.0',
          'browserify-istanbul': '2.0.0',
          'del': '2.0.2',
          'gulp': '3.9.1',
          'gulp-concat': '2.6.0',
          'gulp-help': '1.6.0',
          'gulp-inject': '4.0.0',
          'gulp-jasmine': '2.0.1',
          'gulp-jshint': '2.0.0',
          'gulp-load-plugins': '1.0.0-rc.1',
          'gulp-notify': '2.2.0',
          "gulp-nsp": "2.4.0",
          'gulp-rename': '1.2.2',
          'gulp-size': '2.1.0',
          'gulp-sourcemaps': '1.6.0',
          'gulp-streamify': '1.0.2',
          'gulp-tsc': '1.1.5',
          'gulp-tsconfig-files': '0.0.2',
          'gulp-tslint': '3.1',
          'gulp-typedoc': '1.2.1',
          'gulp-typescript': '2.12.2',
          'gulp-typings': '1.3.4',
          'gulp-uglify': '1.2.0',
          'gulp-util': '3.0.7',
          'jasmine': '2.3.1',
          'jasmine-ajax': '3.2.0',
          'jasmine-core': '2.4.1',
          'jshint': '2.9.1',
          'jshint-stylish': '2.1.0',
          'karma': '0.13.22',
          'karma-browserify': '5.0.3',
          'karma-chrome-launcher': '0.2.2',
          'karma-coverage': '0.5.5',
          'karma-jasmine': '0.3.7',
          'karma-phantomjs-launcher': '1.0.0',
          'karma-remap-istanbul': '0.0.5',
          'path': '0.12.7',
          'phantomjs-prebuilt': '2.1.7',
          'remap-istanbul': '0.5.1',
          'require-dir': '0.3.0',
          'run-sequence': '1.1.5',
          'tsify': '0.14.1',
          'typescript': '1.8.7',
          'vinyl-source-stream': '1.1.0',
          'watchify': '3.7.0'
        },
        scripts: {
          'typings': 'gulp typings-install',
          'test': 'gulp test',
          'dev test': 'gulp dev-test',
          'watch': 'gulp watch',
          'clean': 'gulp clean-build',
          'build': 'gulp build',
          'coverage': 'gulp coverage',
          'doc': 'gulp typedoc'
        }
      });

      if (this.options.styles) {
        pkg.devDependencies['gulp-cssmin'] = '0.1.7';
      }

      if (this.options.styles && this.options.scss) {
        pkg.devDependencies['gulp-sass'] = '2.2.0';
        pkg.devDependencies['gulp-scss-lint'] = '0.3.9';
      }

      if (this.options.styles && !this.options.scss) {
        pkg.devDependencies['gulp-csslint'] = '0.3.0';
      }

      this.fs.writeJSON(this.destinationPath(this.options.generateInto, 'package.json'), pkg);
    },

    gulpfile: function () {
      this.fs.copy(
        this.templatePath('gulpfile.js'),
        this.destinationPath(this.options.generateInto, 'gulpfile.js')
      );

      this.fs.copy(
        this.templatePath('build.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'build.js')
      );

      this.fs.copyTpl(
        this.templatePath('clean.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'clean.js'),
        {
          bower: this.options.bower,
          styles: this.options.styles
        }
      );

      this.fs.copyTpl(
        this.templatePath('conf.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'conf.js'),
        {
          projectName: this.options.name,
          bower: this.options.bower,
          styles: this.options.styles,
          scss: this.options.scss
        }
      );

      if (this.options.styles) {
        this.fs.copyTpl(
          this.templatePath('copy.js'),
          this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'copy.js'),
          {
            bower: this.options.bower
          }
        );
      }

      if (this.options.styles || this.options.bower) {
        this.fs.copyTpl(
          this.templatePath('inject.js'),
          this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'inject.js'),
          {
            bower: this.options.bower,
            styles: this.options.styles
          }
        );
      }

      if (this.options.styles && this.options.scss) {
        this.fs.copy(
          this.templatePath('sass.js'),
          this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'sass.js')
        );
      }

      if (this.options.styles && !this.options.scss) {
        this.fs.copy(
          this.templatePath('css.js'),
          this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'css.js')
        );
      }

      this.fs.copyTpl(
        this.templatePath('scripts.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'scripts.js'),
        {
          bower: this.options.bower,
          styles: this.options.styles,
          scss: this.options.scss
        }
      );

      this.fs.copy(
        this.templatePath('tests.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'tests.js')
      );

      this.fs.copy(
        this.templatePath('tsconfig.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'tsconfig.js')
      );

      this.fs.copy(
        this.templatePath('tsdocs.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'tsdocs.js')
      );

      this.fs.copyTpl(
        this.templatePath('watch.js'),
        this.destinationPath(path.join(this.options.generateInto, 'gulp'), 'watch.js'),
        {
          styles: this.options.styles,
          scss: this.options.scss
        }
      );
    }
  }
});
<|endoftext|>


<commit_before>const HtmlScreenshotReporter = require("protractor-jasmine2-screenshot-reporter");
const JasmineReporters = require('jasmine-reporters');
const SpecReporter = require('jasmine-spec-reporter').SpecReporter;

exports.config = {
    allScriptsTimeout: 120000,

    specs: [
        './e2e/account/*.spec.ts',
        './e2e/admin/*.spec.ts',
        './e2e/entities/*.spec.ts',
        './e2e/scenarios/*.spec.ts'
    ],

    capabilities: {
        'browserName': 'chrome',
        'phantomjs.binary.path': require('phantomjs-prebuilt').path,
        'phantomjs.ghostdriver.cli.args': ['--loglevel=DEBUG'],
        chromeOptions: {
            args: [ "--window-size=1920x980" ]
        }
    },

    directConnect: true,

    baseUrl: 'http://localhost:8080/',

    framework: 'jasmine2',

    jasmineNodeOpts: {
        showColors: true,
        defaultTimeoutInterval: 120000,
        print: function() {}
    },

    beforeLaunch: function() {
        require('ts-node').register({
            project: ''
        });
    },

    onPrepare: function() {
        browser.driver.manage().window().setSize(1280, 1024);
        jasmine.getEnv().addReporter(new JasmineReporters.JUnitXmlReporter({
            savePath: 'build/reports/e2e',
            consolidateAll: false
        }));
        jasmine.getEnv().addReporter(new HtmlScreenshotReporter({
            dest: "build/reports/e2e/screenshots"
        }));
        jasmine.getEnv().addReporter(new SpecReporter({
            spec: {
                displayStacktrace: true
            }
        }));
    },

    useAllAngular2AppRoots: true
};
<commit_msg>Revert protractor.conf.js<commit_after>const HtmlScreenshotReporter = require("protractor-jasmine2-screenshot-reporter");
const JasmineReporters = require('jasmine-reporters');
const SpecReporter = require('jasmine-spec-reporter').SpecReporter;

exports.config = {
    allScriptsTimeout: 120000,

    specs: [
        './e2e/account/*.spec.ts',
        './e2e/admin/*.spec.ts',
        './e2e/entities/*.spec.ts',
        './e2e/scenarios/*.spec.ts'
    ],

    capabilities: {
        'browserName': 'chrome',
        'phantomjs.binary.path': require('phantomjs-prebuilt').path,
        'phantomjs.ghostdriver.cli.args': ['--loglevel=DEBUG'],
        chromeOptions: {
            args: [ "--headless", "--disable-gpu", "--window-size=1280x1024" ]
        }
    },

    directConnect: true,

    baseUrl: 'http://localhost:8080/',

    framework: 'jasmine2',

    jasmineNodeOpts: {
        showColors: true,
        defaultTimeoutInterval: 120000,
        print: function() {}
    },

    beforeLaunch: function() {
        require('ts-node').register({
            project: ''
        });
    },

    onPrepare: function() {
        browser.driver.manage().window().setSize(1280, 1024);
        jasmine.getEnv().addReporter(new JasmineReporters.JUnitXmlReporter({
            savePath: 'build/reports/e2e',
            consolidateAll: false
        }));
        jasmine.getEnv().addReporter(new HtmlScreenshotReporter({
            dest: "build/reports/e2e/screenshots"
        }));
        jasmine.getEnv().addReporter(new SpecReporter({
            spec: {
                displayStacktrace: true
            }
        }));
    },

    useAllAngular2AppRoots: true
};
<|endoftext|>


<commit_before>var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/mydb');

//schema//
var locationSchema = mongoose.Schema({
        city : String
    });
//Model//
var Locations = mongoose.model('location',locationSchema);

var db = mongoose.connection;
db.on('error',console.error.bind(console,'connection error'));
db.once('open',function(callback){
    console.log('Mongodb is connected');
});

module.exports.create = function(nc){
    var cityLocation = new Locations({city:nc})
    console.log(cityLocation.city)
    cityLocation.save(function(err,cityLocation){

        if(!err) return ('The object has been added to the db ...');
    });
}

module.exports.update = function(nc){
}

module.exports.find = function(nc){
    function findCity(_id){
        console.log(_id);
    }
    var cityLocation = Location({_id:name_city});
    cityLocation.findOne(findCity);
}

module.exports.delete = function(nc){
}<commit_msg>module.exports find<commit_after>var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/mydb');

//schema//
var locationSchema = mongoose.Schema({
        city : String
    });
//Model//
var Locations = mongoose.model('location',locationSchema);

var db = mongoose.connection;
db.on('error',console.error.bind(console,'connection error'));
db.once('open',function(callback){
    console.log('Mongodb is connected');
});

module.exports.create = function(nc){
    var cityLocation = new Locations({city:nc})
    console.log(cityLocation.city)
    cityLocation.save(function(err,cityLocation){

        if(!err) return ('The object has been added to the db ...');
    });
}

module.exports.update = function(nc){
}

module.exports.find = function(nc){
    function findCity(_id){
        console.log(_id);
    }
    var cityLocation = Location({_id:name_city});
    cityLocation.find(findCity);
}

module.exports.delete = function(nc){
}<|endoftext|>


<commit_before>'use strict';

const {bts2n, n2bts, N_TO_BT_DIGIT, BT_DIGIT_TO_N} = require('balanced-ternary');
const {get_trit, set_trit, slice_trits} = require('trit-getset');
const ttToUnicode = require('trit-text').toUnicode;

const {TRITS_PER_TRYTE, TRYTES_PER_WORD, TRITS_PER_WORD, MAX_TRYTE, MIN_TRYTE, MEMORY_SIZE} = require('./arch');

const {OP, ADDR_MODE, XOP, XOP_TO_ADDR_MODE_OP} = require('./opcodes');

const {decode_instruction, decode_operand, disasm1} = require('./instr_decode');
const ALU = require('./alu');
const Memory = require('./memory');
const Flags = require('./flags');
const execute_xop_instruction = require('./xop');
const Stack = require('./stack');

class CPU {
  constructor(opts={}) {
    this.memory = opts.memory || Memory({
      tryteCount: MEMORY_SIZE,
      map: opts.memoryMap || {}
    });
    this.pc = 0;
    this.accum = 0;
    this.index = 0;
    this.yindex = 0;
    this.flags = Flags();
    this.stack = Stack(this.memory);
    this.alu = ALU(this);
    this.dnop_throws_enabled = false;

    this.flags.I = -1; // by default only allow int 0, non-maskable NMI/start

    console.log('initial flags=',n2bts(this.flags));
  }

  state_snapshot() {
    return {
      pc: this.pc,
      accum: this.accum,
      index: this.index,
      yindex: this.yindex,
      stackptr: this.stack.stackptr,
      flags: this.flags
    };
  }

  state_restore(state) {
    this.pc = state.pc;
    this.accum = state.accum;
    this.index = state.index;
    this.yindex = state.yindex;
    this.stack.stackptr = state.stackptr;
    this.flags = state.flags;
  }

  read_int_vector(intnum) {
    return this.memory.readWord(this.memory.minAddress + ((intnum + 1) * 2));
  }

  is_interrupt_allowed(intnum) {
    switch(this.flags.I) {
      case -1:
        // I=-1 allow only nonmaskable NMI interrupt 0 (start) (SEIN) (default)
        return intnum === 0;

      case 0:
        // I=0 allow all interrupts (CLI)
        return true;

      case 1:
        // I=1 allow interrupts -1 and 0, but mask 1 (SEIP)
        return intnum !== 1;
    }
  }

  interrupt(intnum, value) {
    console.log('interrupt',intnum,value);
    if (!this.is_interrupt_allowed(intnum)) {
      console.log(`interrupt ${intnum} masked by I=${this.flags.I}`);
      return;
    }

    const before = this.state_snapshot();

    // Read interrupt vector table at negative-most memory, word addresses pointers:
    // iiiii iiiii -29524 int -1
    // iiiii iiii0 -29523
    //
    // iiiii iiii1 -29522 int 0
    // iiiii iii0i -29521
    //
    // iiiii iii00 -29520 int +1
    // iiiii iii01 -29519
    const address = this.read_int_vector(intnum);
    console.log('interrupt vector address',address);

    if (address === 0) { // probably wrong
      debugger;
      throw new Error(`unset interrupt vector for ${intnum}`);
    }

    // Set accumulator to passed in value, used to send data from I/O
    // TODO: other registers? index, yindex, flags; optional. Or at least clear
    if (value !== undefined) this.accum = value;

    // Execute interrupt handler
    this.pc = address;
    this.run();

    // Restore previous state, except NMI/start interrupt, since it can set flags for other interrupt handlers
    if (intnum !== 0) this.state_restore(before);
  }

  execute_branch_instruction(flag, compare, direction, rel_address) {
    console.log(`compare flag=${flag}, direction=${direction}, compare=${compare}`);

    // compare (b) trit to compare flag with
    const flag_value = this.flags.get_flag(flag);

    // direction (c)
    // i less than (flag < trit)
    // 0 equal (flag = trit)
    // 1 not equal (flag != trit)
    let branch_taken = false;
    if (direction === -1) {
      branch_taken = flag_value < compare;
    } else if (direction === 0) {
      branch_taken = flag_value === compare;
    } else if (direction === 1) {
      branch_taken = flag_value !== compare;
    }

    console.log(`flag flag_value=${flag_value}, branch_taken=${branch_taken}, rel_address=${rel_address}`);

    // if matches, relative branch (+/- 121)
    if (branch_taken) {
      console.log('taking branch from',this.pc,'to',this.pc+rel_address);
      this.pc += rel_address;
    } else {
      console.log('not taking branch from',this.pc,'to',this.pc+rel_address);
    }
  }

  // Read instruction operand from decoded instruction, return read/write accessors
  read_alu_operand(di) {
    let read_arg, write_arg, address_of_arg;

    let decoded_operand = decode_operand(di, this.memory.subarray(this.pc), 0);

    this.pc += decoded_operand.consumed * this.flags.R;

    switch(decoded_operand.addressing_mode) {
      case ADDR_MODE.ABSOLUTE:
        // absolute, 2-tryte address
        console.log('absolute',decoded_operand.value);

        read_arg = () => { return this.memory.read(decoded_operand.value); };
        write_arg = (value) => { return this.memory.write(decoded_operand.value, value); };
        address_of_arg = () => { return decoded_operand.value; };

        break;

      case ADDR_MODE.ABSOLUTE_X:
        console.log('absolute,x',decoded_operand.value);

        read_arg = () => { return this.memory.read(decoded_operand.value + this.index); };
        write_arg = (value) => { return this.memory.write(decoded_operand.value + this.index, value); };
        address_of_arg = () => { return decoded_operand.value + this.index; };

        break;

      case ADDR_MODE.ABSOLUTE_Y:
        console.log('absolute,y',decoded_operand.value);

        read_arg = () => { return this.memory.read(decoded_operand.value + this.yindex); };
        write_arg = (value) => { return this.memory.write(decoded_operand.value + this.yindex, value); };
        address_of_arg = () => { return decoded_operand.value + this.yindex; };

        break;

      case ADDR_MODE.ACCUMULATOR:
        // accumulator, register, no arguments
        read_arg = () => { return this.accum; };
        write_arg = (value) => { return (this.accum = value); };
        address_of_arg = () => { throw new Error(`cannot take address of accumulator, in instruction ${JSON.stringify(di)} at pc=${this.pc}`); };

        console.log('accum');

        break;

      case ADDR_MODE.IMMEDIATE:
        // immediate, 1-tryte literal
        console.log('immediate',decoded_operand.value);

        read_arg = () => { return decoded_operand.value; };
        write_arg = () => { throw new Error(`cannot write to immediate: ${decoded_operand.value}, in instruction ${JSON.stringify(di)} at pc=${this.pc}`); };
        address_of_arg = () => { throw new Error(`cannot take address of immediate operand, in instruction ${JSON.stringify(di)} at pc=${this.pc}`); }; // actually, maybe can (code_offset)

        break;

      case ADDR_MODE.INDIRECT_INDEXED_Y:
        console.log('indirect_indexed',decoded_operand.value);

        address_of_arg = () => {
          // (indirect),Y
          let ptr = this.memory.readWord(decoded_operand.value);
          ptr += this.yindex;
          return ptr;
        };

        read_arg = () => { return this.memory.read(address_of_arg()); };
        write_arg = (value) => { return this.memory.write(address_of_arg(), value); }

        break;

      default:
        read_arg = write_arg = address_of_arg = () => { throw new Error(`unimplemented addressing mode ${decoded_operand.addressing_mode}, in decoded=operand${JSON.stringify(di)}`); }

    }

    return {read_arg, write_arg, address_of_arg};
  }

  execute_next_instruction() {
    const opcode = this.memory.read(this.pc);
    console.log('\npc=',this.pc,' opcode=',opcode,'disasm=',disasm1(this.memory.subarray(this.pc)).asm);

    if (opcode === undefined) {
      // increase MEMORY_SIZE if running out too often
      throw new Error('program counter '+this.pc+' out of range into undefined memory');
    }
    if (opcode > MAX_TRYTE || opcode < MIN_TRYTE) {
      // indicates internal error in simulator, backing store shouldn't be written out of this range
      throw new Error('memory at pc='+this.pc+' value='+opcode+' out of 5-trit range');
    }

    const di = decode_instruction(opcode);

    if (di.family === 0) {
      let {read_arg, write_arg, address_of_arg} = this.read_alu_operand(di);

      this.alu.execute_alu_instruction(di.operation, read_arg, write_arg);
    } else if (di.family === 1) {
      const rel_address = this.memory.read(this.pc += this.flags.R);

      this.execute_branch_instruction(di.flag, di.compare, di.direction, rel_address);
    } else if (di.family === -1) {
      let {read_arg, write_arg, address_of_arg} = this.read_alu_operand(di);

      if (XOP_TO_ADDR_MODE_OP[di.operation] !== undefined) {
        // alu operation, extended addressing mode in xop namespace
        const alu_op = XOP_TO_ADDR_MODE_OP[di.operation][0];
        this.alu.execute_alu_instruction(alu_op, read_arg, write_arg);
      } else {
        execute_xop_instruction(this, di.operation, read_arg, write_arg, address_of_arg);
      }
    }

    console.log('flags:','RHUVSDCIL');
    console.log('flags:',n2bts(this.flags.value), `A=${this.accum}(${ttToUnicode(this.accum)}), X=${this.index}, Y=${this.yindex}`);
  }

  step() {
    this.execute_next_instruction();
    this.pc += this.flags.R;
  }

  run() {
    this.flags.R = 1; // running: 1, program counter increments by; -1 runs backwards, 0 halts
    do {
      this.step();
    } while(this.flags.R !== 0);
    console.log('Halted with status',this.flags.H);
  }
}

module.exports = function(opts) {
  return new CPU(opts);
};

<commit_msg>Enable DNOP throws by default (dnop_throws_enabled)<commit_after>'use strict';

const {bts2n, n2bts, N_TO_BT_DIGIT, BT_DIGIT_TO_N} = require('balanced-ternary');
const {get_trit, set_trit, slice_trits} = require('trit-getset');
const ttToUnicode = require('trit-text').toUnicode;

const {TRITS_PER_TRYTE, TRYTES_PER_WORD, TRITS_PER_WORD, MAX_TRYTE, MIN_TRYTE, MEMORY_SIZE} = require('./arch');

const {OP, ADDR_MODE, XOP, XOP_TO_ADDR_MODE_OP} = require('./opcodes');

const {decode_instruction, decode_operand, disasm1} = require('./instr_decode');
const ALU = require('./alu');
const Memory = require('./memory');
const Flags = require('./flags');
const execute_xop_instruction = require('./xop');
const Stack = require('./stack');

class CPU {
  constructor(opts={}) {
    this.memory = opts.memory || Memory({
      tryteCount: MEMORY_SIZE,
      map: opts.memoryMap || {}
    });
    this.pc = 0;
    this.accum = 0;
    this.index = 0;
    this.yindex = 0;
    this.flags = Flags();
    this.stack = Stack(this.memory);
    this.alu = ALU(this);
    this.dnop_throws_enabled = true;

    this.flags.I = -1; // by default only allow int 0, non-maskable NMI/start

    console.log('initial flags=',n2bts(this.flags));
  }

  state_snapshot() {
    return {
      pc: this.pc,
      accum: this.accum,
      index: this.index,
      yindex: this.yindex,
      stackptr: this.stack.stackptr,
      flags: this.flags
    };
  }

  state_restore(state) {
    this.pc = state.pc;
    this.accum = state.accum;
    this.index = state.index;
    this.yindex = state.yindex;
    this.stack.stackptr = state.stackptr;
    this.flags = state.flags;
  }

  read_int_vector(intnum) {
    return this.memory.readWord(this.memory.minAddress + ((intnum + 1) * 2));
  }

  is_interrupt_allowed(intnum) {
    switch(this.flags.I) {
      case -1:
        // I=-1 allow only nonmaskable NMI interrupt 0 (start) (SEIN) (default)
        return intnum === 0;

      case 0:
        // I=0 allow all interrupts (CLI)
        return true;

      case 1:
        // I=1 allow interrupts -1 and 0, but mask 1 (SEIP)
        return intnum !== 1;
    }
  }

  interrupt(intnum, value) {
    console.log('interrupt',intnum,value);
    if (!this.is_interrupt_allowed(intnum)) {
      console.log(`interrupt ${intnum} masked by I=${this.flags.I}`);
      return;
    }

    const before = this.state_snapshot();

    // Read interrupt vector table at negative-most memory, word addresses pointers:
    // iiiii iiiii -29524 int -1
    // iiiii iiii0 -29523
    //
    // iiiii iiii1 -29522 int 0
    // iiiii iii0i -29521
    //
    // iiiii iii00 -29520 int +1
    // iiiii iii01 -29519
    const address = this.read_int_vector(intnum);
    console.log('interrupt vector address',address);

    if (address === 0) { // probably wrong
      debugger;
      throw new Error(`unset interrupt vector for ${intnum}`);
    }

    // Set accumulator to passed in value, used to send data from I/O
    // TODO: other registers? index, yindex, flags; optional. Or at least clear
    if (value !== undefined) this.accum = value;

    // Execute interrupt handler
    this.pc = address;
    this.run();

    // Restore previous state, except NMI/start interrupt, since it can set flags for other interrupt handlers
    if (intnum !== 0) this.state_restore(before);
  }

  execute_branch_instruction(flag, compare, direction, rel_address) {
    console.log(`compare flag=${flag}, direction=${direction}, compare=${compare}`);

    // compare (b) trit to compare flag with
    const flag_value = this.flags.get_flag(flag);

    // direction (c)
    // i less than (flag < trit)
    // 0 equal (flag = trit)
    // 1 not equal (flag != trit)
    let branch_taken = false;
    if (direction === -1) {
      branch_taken = flag_value < compare;
    } else if (direction === 0) {
      branch_taken = flag_value === compare;
    } else if (direction === 1) {
      branch_taken = flag_value !== compare;
    }

    console.log(`flag flag_value=${flag_value}, branch_taken=${branch_taken}, rel_address=${rel_address}`);

    // if matches, relative branch (+/- 121)
    if (branch_taken) {
      console.log('taking branch from',this.pc,'to',this.pc+rel_address);
      this.pc += rel_address;
    } else {
      console.log('not taking branch from',this.pc,'to',this.pc+rel_address);
    }
  }

  // Read instruction operand from decoded instruction, return read/write accessors
  read_alu_operand(di) {
    let read_arg, write_arg, address_of_arg;

    let decoded_operand = decode_operand(di, this.memory.subarray(this.pc), 0);

    this.pc += decoded_operand.consumed * this.flags.R;

    switch(decoded_operand.addressing_mode) {
      case ADDR_MODE.ABSOLUTE:
        // absolute, 2-tryte address
        console.log('absolute',decoded_operand.value);

        read_arg = () => { return this.memory.read(decoded_operand.value); };
        write_arg = (value) => { return this.memory.write(decoded_operand.value, value); };
        address_of_arg = () => { return decoded_operand.value; };

        break;

      case ADDR_MODE.ABSOLUTE_X:
        console.log('absolute,x',decoded_operand.value);

        read_arg = () => { return this.memory.read(decoded_operand.value + this.index); };
        write_arg = (value) => { return this.memory.write(decoded_operand.value + this.index, value); };
        address_of_arg = () => { return decoded_operand.value + this.index; };

        break;

      case ADDR_MODE.ABSOLUTE_Y:
        console.log('absolute,y',decoded_operand.value);

        read_arg = () => { return this.memory.read(decoded_operand.value + this.yindex); };
        write_arg = (value) => { return this.memory.write(decoded_operand.value + this.yindex, value); };
        address_of_arg = () => { return decoded_operand.value + this.yindex; };

        break;

      case ADDR_MODE.ACCUMULATOR:
        // accumulator, register, no arguments
        read_arg = () => { return this.accum; };
        write_arg = (value) => { return (this.accum = value); };
        address_of_arg = () => { throw new Error(`cannot take address of accumulator, in instruction ${JSON.stringify(di)} at pc=${this.pc}`); };

        console.log('accum');

        break;

      case ADDR_MODE.IMMEDIATE:
        // immediate, 1-tryte literal
        console.log('immediate',decoded_operand.value);

        read_arg = () => { return decoded_operand.value; };
        write_arg = () => { throw new Error(`cannot write to immediate: ${decoded_operand.value}, in instruction ${JSON.stringify(di)} at pc=${this.pc}`); };
        address_of_arg = () => { throw new Error(`cannot take address of immediate operand, in instruction ${JSON.stringify(di)} at pc=${this.pc}`); }; // actually, maybe can (code_offset)

        break;

      case ADDR_MODE.INDIRECT_INDEXED_Y:
        console.log('indirect_indexed',decoded_operand.value);

        address_of_arg = () => {
          // (indirect),Y
          let ptr = this.memory.readWord(decoded_operand.value);
          ptr += this.yindex;
          return ptr;
        };

        read_arg = () => { return this.memory.read(address_of_arg()); };
        write_arg = (value) => { return this.memory.write(address_of_arg(), value); }

        break;

      default:
        read_arg = write_arg = address_of_arg = () => { throw new Error(`unimplemented addressing mode ${decoded_operand.addressing_mode}, in decoded=operand${JSON.stringify(di)}`); }

    }

    return {read_arg, write_arg, address_of_arg};
  }

  execute_next_instruction() {
    const opcode = this.memory.read(this.pc);
    console.log('\npc=',this.pc,' opcode=',opcode,'disasm=',disasm1(this.memory.subarray(this.pc)).asm);

    if (opcode === undefined) {
      // increase MEMORY_SIZE if running out too often
      throw new Error('program counter '+this.pc+' out of range into undefined memory');
    }
    if (opcode > MAX_TRYTE || opcode < MIN_TRYTE) {
      // indicates internal error in simulator, backing store shouldn't be written out of this range
      throw new Error('memory at pc='+this.pc+' value='+opcode+' out of 5-trit range');
    }

    const di = decode_instruction(opcode);

    if (di.family === 0) {
      let {read_arg, write_arg, address_of_arg} = this.read_alu_operand(di);

      this.alu.execute_alu_instruction(di.operation, read_arg, write_arg);
    } else if (di.family === 1) {
      const rel_address = this.memory.read(this.pc += this.flags.R);

      this.execute_branch_instruction(di.flag, di.compare, di.direction, rel_address);
    } else if (di.family === -1) {
      let {read_arg, write_arg, address_of_arg} = this.read_alu_operand(di);

      if (XOP_TO_ADDR_MODE_OP[di.operation] !== undefined) {
        // alu operation, extended addressing mode in xop namespace
        const alu_op = XOP_TO_ADDR_MODE_OP[di.operation][0];
        this.alu.execute_alu_instruction(alu_op, read_arg, write_arg);
      } else {
        execute_xop_instruction(this, di.operation, read_arg, write_arg, address_of_arg);
      }
    }

    console.log('flags:','RHUVSDCIL');
    console.log('flags:',n2bts(this.flags.value), `A=${this.accum}(${ttToUnicode(this.accum)}), X=${this.index}, Y=${this.yindex}`);
  }

  step() {
    this.execute_next_instruction();
    this.pc += this.flags.R;
  }

  run() {
    this.flags.R = 1; // running: 1, program counter increments by; -1 runs backwards, 0 halts
    do {
      this.step();
    } while(this.flags.R !== 0);
    console.log('Halted with status',this.flags.H);
  }
}

module.exports = function(opts) {
  return new CPU(opts);
};

<|endoftext|>


<commit_before>/*
 * Verwaltet Server-(Ajax)-Requests. Antworten vom Server werden als JSON
 * erwartet.
 *
 * @author Philipp Kemmeter
 */

/** Alles statisch */
GE.Ajax = {};

/** Welche Funktion bei erfolgreichem Request aufgerufen werden soll */
GE.Ajax.callback_functions = [];

/**
 * Der Name der Session, den der Server vergeben hat (muss mitgesandt werden
 * zur Identifikation der Verbindung
 */
GE.Ajax.session_name = 'sid';

/**
 * Die ID der Session, den der Server vergeben hat (muss mitgesandt werden
 * zur Identifikation der Verbindung
 */
GE.Ajax.session_id = '';

/**
 * Array von XHR-Objekten. Immer, wenn gerade eines in Gebrauch
 * ist, wird ein neues erzeugt. Alte, ungenutzte, werden dann wieder
 * freigegeben und deren Slots können erneut benutzt werden.
 */
GE.Ajax.xml_http_requests = [];

/**
 * Array, das angibt, ob das XHR-Objekt in xml_http_requests mit demselben
 * Index gerade in gebrauch ist, oder nicht.
 */
GE.Ajax.xhr_available = [];

/**
 * Anzahl der Requests. Wird beim Abschicken hochgezählt, damit Cachen
 * vermieden wird.
 */
GE.Ajax.request_num = 0;

/**
 * Gibt ein neu erzeugtes XHR-Objekt zurück
 *
 * @return XMLHttpRequest
 * @throws Error
 */
GE.Ajax.create_new_request = function() {
	if (typeof XMLHttpRequest != 'undefined')
		return new XMLHttpRequest();
	else if (window.ActiveXObject) {
		var avers = ["Microsoft.XmlHttp", "MSXML2.XmlHttp",
		             "MSXML2.XmlHttp.3.0",  "MSXML2.XmlHttp.4.0",
		             "MSXML2.XmlHttp.5.0"];
		for (var i = avers.length -1; i >= 0; i--) {
			try {
				httpObj = new ActiveXObject(avers[i]);
				return httpObj;
			} catch(e) {}
		}
	}
	throw new Error('XMLHttp (AJAX) not supported');
};

/**
 * Gibt den Index des nächsten freien XHR-Objekts zurück
 *
 * @access private
 * @return uint
 * @throws Error
 */
GE.Ajax.get_xhr_index = function() {
	var n = GE.Ajax.xhr_available.length;
	var result;
	for (var i = 0; i < n; i++) {
		if (GE.Ajax.xhr_available[i]) {
			result = i;
			break;
		}
	}

	if (i == n) {
		// Wenn kein neues gefunden werden konnte, dann ein neues erzeugen
		GE.Ajax.xml_http_requests[n] =
			GE.Ajax.create_new_request();
		result = n;
	}
	// und als "not available" markieren
	GE.Ajax.xhr_available[result] = false;

	return result;
};

/**
 * Sendet einen Request an den durch die URL bestimmten Server. Wird post_data
 * angegeben, werden sie als POST-Daten versandt, ansonsten wird die URL
 * inklusive aller mitgelieferten Parameter als GET versandt.
 * Die angegebene callback_function wird dann bei Erfolg aufgerufen.
 *
 * @param string url					Bestimmt das Ziel des Requests
 * @param Function callback_function	Welche Funktion bei Erfolg aufzurufen
 *                                      ist
 * @param string post_data[optional]	ggf. POST-Daten zum Versenden
 * @param bool no_parse[=false]			Ob die Serverantwort geparst werden
 *                                      soll
 * @throws Error						wenn Request nicht gesendet werden
 *                                      konnte
 */

GE.Ajax.send_request = function(url, callback_function, post_data, no_parse) {
	var xhri = GE.Ajax.get_xhr_index();
	var xhr = GE.Ajax.xml_http_requests[xhri];

	GE.Ajax.callback_functions[xhri] = callback_function;
	var method = !post_data ? "GET" : "POST";

	url = (url.indexOf('?') == -1) ? url + '?' : url + '&';
	url += "anti_cache=" + GE.Ajax.request_num;
	if (GE.Ajax.session_name && GE.Ajax.session_id)
		url += "&"+GE.Ajax.session_name + "="
				+ GE.Ajax.session_id;
	xhr.open(method, url, true);
	xhr.onreadystatechange = function() {
		var my_xhr = GE.Ajax.xml_http_requests[xhri];
		if ((my_xhr.readyState == 4) && (my_xhr.status == 200)) {
			GE.Ajax.xhr_available[xhri] = true;

			if (GE.Ajax.callback_functions[xhri]) {
				if(no_parse) {
					GE.Ajax.callback_functions[xhri].call(this,
						null, my_xhr.responseText);
				}
				else {
					if (JSON && JSON.parse)
						var o = JSON.parse(my_xhr.responseText);
					else
						var o = eval('(' + my_xhr.responseText + ')');

					if (o.sid)
						GE.Ajax.session_id = o.sid;

					GE.Ajax.callback_functions[xhri].call(this,
						o.err, o.result);
				}
			};
		}
	};
	if (method == "POST") {
		xhr.setRequestHeader("Content-type",
			"application/x-www-form-urlencoded;charset=UTF-8");
		/* Unsafe: Wird von modernen Browser nicht erlaubt
		 * xhr.setRequestHeader("Content-length", post_data.length);
		 */
		xhr.send(encodeURI(post_data));
	}
	else {
		xhr.setRequestHeader("Content-type",
			"text/plain;charset=UTF-8");
		/* Unsafe: Wird von modernen Browser nicht erlaubt
		 * xhr.setRequestHeader("Content-length", 0);
		 */
		xhr.send(null);
	}
	GE.Ajax.request_num++;
	return true;
};

<commit_msg>minor fix for ie<commit_after>/*
 * Verwaltet Server-(Ajax)-Requests. Antworten vom Server werden als JSON
 * erwartet.
 *
 * @author Philipp Kemmeter
 */

/** Alles statisch */
GE.Ajax = {};

/** Welche Funktion bei erfolgreichem Request aufgerufen werden soll */
GE.Ajax.callback_functions = [];

/**
 * Der Name der Session, den der Server vergeben hat (muss mitgesandt werden
 * zur Identifikation der Verbindung
 */
GE.Ajax.session_name = 'sid';

/**
 * Die ID der Session, den der Server vergeben hat (muss mitgesandt werden
 * zur Identifikation der Verbindung
 */
GE.Ajax.session_id = '';

/**
 * Array von XHR-Objekten. Immer, wenn gerade eines in Gebrauch
 * ist, wird ein neues erzeugt. Alte, ungenutzte, werden dann wieder
 * freigegeben und deren Slots können erneut benutzt werden.
 */
GE.Ajax.xml_http_requests = [];

/**
 * Array, das angibt, ob das XHR-Objekt in xml_http_requests mit demselben
 * Index gerade in gebrauch ist, oder nicht.
 */
GE.Ajax.xhr_available = [];

/**
 * Anzahl der Requests. Wird beim Abschicken hochgezählt, damit Cachen
 * vermieden wird.
 */
GE.Ajax.request_num = 0;

/**
 * Gibt ein neu erzeugtes XHR-Objekt zurück
 *
 * @return XMLHttpRequest
 * @throws Error
 */
GE.Ajax.create_new_request = function() {
	if (typeof XMLHttpRequest != 'undefined')
		return new XMLHttpRequest();
	else if (window.ActiveXObject) {
		var avers = ["Microsoft.XmlHttp", "MSXML2.XmlHttp",
		             "MSXML2.XmlHttp.3.0",  "MSXML2.XmlHttp.4.0",
		             "MSXML2.XmlHttp.5.0"];
		for (var i = avers.length -1; i >= 0; i--) {
			try {
				httpObj = new ActiveXObject(avers[i]);
				return httpObj;
			} catch(e) {}
		}
	}
	throw new Error('XMLHttp (AJAX) not supported');
};

/**
 * Gibt den Index des nächsten freien XHR-Objekts zurück
 *
 * @access private
 * @return uint
 * @throws Error
 */
GE.Ajax.get_xhr_index = function() {
	var n = GE.Ajax.xhr_available.length;
	var result;
	for (var i = 0; i < n; i++) {
		if (GE.Ajax.xhr_available[i]) {
			result = i;
			break;
		}
	}

	if (i == n) {
		// Wenn kein neues gefunden werden konnte, dann ein neues erzeugen
		GE.Ajax.xml_http_requests[n] =
			GE.Ajax.create_new_request();
		result = n;
	}
	// und als "not available" markieren
	GE.Ajax.xhr_available[result] = false;

	return result;
};

/**
 * Sendet einen Request an den durch die URL bestimmten Server. Wird post_data
 * angegeben, werden sie als POST-Daten versandt, ansonsten wird die URL
 * inklusive aller mitgelieferten Parameter als GET versandt.
 * Die angegebene callback_function wird dann bei Erfolg aufgerufen.
 *
 * @param string url					Bestimmt das Ziel des Requests
 * @param Function callback_function	Welche Funktion bei Erfolg aufzurufen
 *                                      ist
 * @param string post_data[optional]	ggf. POST-Daten zum Versenden
 * @param bool no_parse[=false]			Ob die Serverantwort geparst werden
 *                                      soll
 * @throws Error						wenn Request nicht gesendet werden
 *                                      konnte
 */

GE.Ajax.send_request = function(url, callback_function, post_data, no_parse) {
	var xhri = GE.Ajax.get_xhr_index();
	var xhr = GE.Ajax.xml_http_requests[xhri];

	GE.Ajax.callback_functions[xhri] = callback_function;
	var method = typeof(post_data == 'undefined') ? "GET" : "POST";

	url = (url.indexOf('?') == -1) ? url + '?' : url + '&';
	url += "anti_cache=" + GE.Ajax.request_num;
	if (GE.Ajax.session_name && GE.Ajax.session_id)
		url += "&"+GE.Ajax.session_name + "="
				+ GE.Ajax.session_id;
	xhr.open(method, url, true);
	xhr.onreadystatechange = function() {
		var my_xhr = GE.Ajax.xml_http_requests[xhri];
		if ((my_xhr.readyState == 4) && (my_xhr.status == 200)) {
			GE.Ajax.xhr_available[xhri] = true;

			if (GE.Ajax.callback_functions[xhri]) {
				if(no_parse) {
					GE.Ajax.callback_functions[xhri].call(this,
						null, my_xhr.responseText);
				}
				else {
					if (JSON && JSON.parse)
						var o = JSON.parse(my_xhr.responseText);
					else
						var o = eval('(' + my_xhr.responseText + ')');

					if (o.sid)
						GE.Ajax.session_id = o.sid;

					GE.Ajax.callback_functions[xhri].call(this,
						o.err, o.result);
				}
			};
		}
	};
	if (method == "POST") {
		xhr.setRequestHeader("Content-type",
			"application/x-www-form-urlencoded;charset=UTF-8");
		/* Unsafe: Wird von modernen Browser nicht erlaubt
		 * xhr.setRequestHeader("Content-length", post_data.length);
		 */
		xhr.send(encodeURI(post_data));
	}
	else {
		xhr.setRequestHeader("Content-type",
			"text/plain;charset=UTF-8");
		/* Unsafe: Wird von modernen Browser nicht erlaubt
		 * xhr.setRequestHeader("Content-length", 0);
		 */
		xhr.send(null);
	}
	GE.Ajax.request_num++;
	return true;
};

<|endoftext|>