Skip to content

Commit

Permalink
Merge pull request #29980 from code-dot-org/dtp_candidate_aaf0b2f5
Browse files Browse the repository at this point in the history
DTP (Test > Production: aaf0b2f)
  • Loading branch information
islemaster committed Jul 26, 2019
2 parents 23b5355 + 92a33e1 commit 03fce26
Show file tree
Hide file tree
Showing 383 changed files with 17,818 additions and 4,550 deletions.
16 changes: 0 additions & 16 deletions apps/src/applab/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -499,22 +499,6 @@ export function penRGB(r, g, b, a) {
return Applab.executeCmd(null, 'penRGB', {r: r, g: g, b: b, a: a});
}

export function insertItem(array, index, item) {
return Applab.executeCmd(null, 'insertItem', {
array: array,
index: index,
item: item
});
}

export function appendItem(array, item) {
return Applab.executeCmd(null, 'appendItem', {array: array, item: item});
}

export function removeItem(array, index) {
return Applab.executeCmd(null, 'removeItem', {array: array, index: index});
}

export function drawChart(chartId, chartType, chartData, options, callback) {
return Applab.executeCmd(null, 'drawChart', {
chartId: chartId,
Expand Down
263 changes: 188 additions & 75 deletions apps/src/applab/dontMarshalApi.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
import {OPTIONAL, outputWarning} from '../lib/util/javascriptMode';
import {
OPTIONAL,
apiValidateType,
outputWarning
} from '../lib/util/javascriptMode';

// APIs designed specifically to run on interpreter data structures without marshalling
// (valuable for performance or to support in/out parameters)
//
// dropletConfig for each of these APIs should be marked with dontMarshal:true

function dmapiValidateType(funcName, varName, varValue, expectedType, opt) {
/*
* The interpreter functions are exported under a single 'default' object
*
* The same functions are exported individually with an extra final parameter
* named calledWithinInterpreter. When called outside of the interpreter without
* the final parameter, the functions will behave as expected in a normal JS
* environment and will not use interpreter-specific data structures.
*
* This allows us to share a single implementation for both the normal versions
* used by exported apps and for the interpreter optimized versions.
*
* To import the interpreter versions:
* import dontMarshalApi from './dontMarshalApi'
*
* To import the normal JavaScript versions:
* import * as dontMarshalApi from './dontMarshalApi'
*/

function dmapiValidateType(_, funcName, varName, varValue, expectedType, opt) {
var properType;
if (typeof varValue !== 'undefined') {
if (expectedType === 'number') {
Expand Down Expand Up @@ -42,47 +64,84 @@ function dmapiValidateType(funcName, varName, varValue, expectedType, opt) {

// Array functions

var getInt = function(obj, def) {
var getInt = function(obj, def, calledWithinInterpreter) {
// Return an integer, or the default.
var n = obj ? Math.floor(obj.toNumber()) : def;
let n;
if (calledWithinInterpreter) {
n = obj ? Math.floor(obj.toNumber()) : def;
} else {
n = typeof obj !== 'undefined' ? Math.floor(obj) : def;
}
if (isNaN(n)) {
n = def;
}
return n;
};

export function insertItem(array, index, item) {
dmapiValidateType('insertItem', 'list', array, 'array');
dmapiValidateType('insertItem', 'index', index, 'number');
function interpreterInsertItem(array, index, item) {
insertItem(array, index, item, dmapiValidateType, true);
}

export function insertItem(
array,
index,
item,
validateType = apiValidateType,
calledWithinInterpreter
) {
validateType({}, 'insertItem', 'list', array, 'array');
validateType({}, 'insertItem', 'index', index, 'number');

index = getInt(index, 0);
const arrayValues = calledWithinInterpreter ? array.properties : array;

index = getInt(index, 0, calledWithinInterpreter);
if (index < 0) {
index = Math.max(array.length + index, 0);
} else {
index = Math.min(index, array.length);
}
// Insert item.
for (var i = array.length - 1; i >= index; i--) {
array.properties[i + 1] = array.properties[i];
arrayValues[i + 1] = arrayValues[i];
}
if (calledWithinInterpreter) {
// In the interpreter, we must manually update the length property:
array.length += 1;
}
array.length += 1;
array.properties[index] = item;
arrayValues[index] = item;
}

function interpreterRemoveItem(array, index) {
removeItem(array, index, dmapiValidateType, true);
}

export function removeItem(array, index) {
dmapiValidateType('removeItem', 'list', array, 'array');
dmapiValidateType('removeItem', 'index', index, 'number');
export function removeItem(
array,
index,
validateType = apiValidateType,
calledWithinInterpreter
) {
validateType({}, 'removeItem', 'list', array, 'array');
validateType({}, 'removeItem', 'index', index, 'number');

index = getInt(index, 0);
const arrayValues = calledWithinInterpreter ? array.properties : array;

index = getInt(index, 0, calledWithinInterpreter);
if (index < 0) {
index = Math.max(array.length + index, 0);
}
// Remove by shifting items after index downward.
for (var i = index; i < array.length - 1; i++) {
array.properties[i] = array.properties[i + 1];
arrayValues[i] = arrayValues[i + 1];
}
if (index < array.length) {
delete array.properties[array.length - 1];
if (calledWithinInterpreter) {
// In the interpreter, the array is not a real array, so
// simply reducing the length of the array is not enough. We must
// delete the object stored as the last element before we
// modify the length:
delete arrayValues[array.length - 1];
}
array.length -= 1;
} else {
// index is out of bounds (too large):
Expand All @@ -96,83 +155,137 @@ export function removeItem(array, index) {
}
}

export function appendItem(array, item) {
dmapiValidateType('appendItem', 'list', array, 'array');

array.properties[array.length] = item;
array.length++;
return window.Applab.JSInterpreter.createPrimitive(array.length);
function interpreterAppendItem(array, item) {
return appendItem(array, item, dmapiValidateType, true);
}

// ImageData RGB helper functions
export function appendItem(
array,
item,
validateType = apiValidateType,
calledWithinInterpreter
) {
validateType({}, 'appendItem', 'list', array, 'array');

// TODO: more parameter validation (data array type, length), error output
const arrayValues = calledWithinInterpreter ? array.properties : array;

export function getRed(imageData, x, y) {
if (imageData.properties.data && imageData.properties.width) {
var pixelOffset = y * imageData.properties.width * 4 + x * 4;
return imageData.properties.data.properties[pixelOffset].toNumber();
arrayValues[array.length] = item;
if (calledWithinInterpreter) {
// In the interpreter, we must manually update the length property:
array.length++;
// And we must create an interpreter primitive to wrap the return value:
return window.Applab.JSInterpreter.createPrimitive(array.length);
} else {
return array.length;
}
}

export function getGreen(imageData, x, y) {
if (imageData.properties.data && imageData.properties.width) {
var pixelOffset = y * imageData.properties.width * 4 + x * 4;
return imageData.properties.data.properties[pixelOffset + 1].toNumber();
}
}
// ImageData RGB helper functions

export function getBlue(imageData, x, y) {
if (imageData.properties.data && imageData.properties.width) {
var pixelOffset = y * imageData.properties.width * 4 + x * 4;
return imageData.properties.data.properties[pixelOffset + 2].toNumber();
}
}
// TODO: more parameter validation (data array type, length), error output

export function getAlpha(imageData, x, y) {
if (imageData.properties.data && imageData.properties.width) {
var pixelOffset = y * imageData.properties.width * 4 + x * 4;
return imageData.properties.data.properties[pixelOffset + 3].toNumber();
function getImageDataValue(
calledWithinInterpreter,
colorOffset,
imageData,
x,
y
) {
const imageDataProperties = calledWithinInterpreter
? imageData.properties
: imageData;
if (imageDataProperties.data && imageDataProperties.width) {
const pixelOffset = y * imageDataProperties.width * 4 + x * 4;
const totalOffset = pixelOffset + colorOffset;
return calledWithinInterpreter
? imageDataProperties.data.properties[totalOffset].toNumber()
: imageDataProperties.data[totalOffset];
}
}

export function setRed(imageData, x, y, value) {
if (imageData.properties.data && imageData.properties.width) {
var pixelOffset = y * imageData.properties.width * 4 + x * 4;
imageData.properties.data.properties[pixelOffset] = value;
}
}
const interpreterGetRed = getImageDataValue.bind(null, true, 0);
const interpreterGetGreen = getImageDataValue.bind(null, true, 1);
const interpreterGetBlue = getImageDataValue.bind(null, true, 2);
const interpreterGetAlpha = getImageDataValue.bind(null, true, 3);

export function setGreen(imageData, x, y, value) {
if (imageData.properties.data && imageData.properties.width) {
var pixelOffset = y * imageData.properties.width * 4 + x * 4;
imageData.properties.data.properties[pixelOffset + 1] = value;
}
}
export const getRed = getImageDataValue.bind(null, false, 0);
export const getGreen = getImageDataValue.bind(null, false, 1);
export const getBlue = getImageDataValue.bind(null, false, 2);
export const getAlpha = getImageDataValue.bind(null, false, 3);

export function setBlue(imageData, x, y, value) {
if (imageData.properties.data && imageData.properties.width) {
var pixelOffset = y * imageData.properties.width * 4 + x * 4;
imageData.properties.data.properties[pixelOffset + 2] = value;
function setImageDataValue(
calledWithinInterpreter,
colorOffset,
imageData,
x,
y,
value
) {
const imageDataProperties = calledWithinInterpreter
? imageData.properties
: imageData;
if (imageDataProperties.data && imageDataProperties.width) {
const pixelOffset = y * imageDataProperties.width * 4 + x * 4;
const totalOffset = pixelOffset + colorOffset;
if (calledWithinInterpreter) {
imageDataProperties.data.properties[totalOffset] = value;
} else {
imageDataProperties.data[totalOffset] = value;
}
}
}

export function setAlpha(imageData, x, y, value) {
if (imageData.properties.data && imageData.properties.width) {
var pixelOffset = y * imageData.properties.width * 4 + x * 4;
imageData.properties.data.properties[pixelOffset + 3] = value;
}
const interpreterSetRed = setImageDataValue.bind(null, true, 0);
const interpreterSetGreen = setImageDataValue.bind(null, true, 1);
const interpreterSetBlue = setImageDataValue.bind(null, true, 2);
const interpreterSetAlpha = setImageDataValue.bind(null, true, 3);

export const setRed = setImageDataValue.bind(null, false, 0);
export const setGreen = setImageDataValue.bind(null, false, 1);
export const setBlue = setImageDataValue.bind(null, false, 2);
export const setAlpha = setImageDataValue.bind(null, false, 3);

function interpreterSetRGB(imageData, x, y, r, g, b, a) {
setRGB(imageData, x, y, r, g, b, a, true);
}

export function setRGB(imageData, x, y, r, g, b, a) {
if (imageData.properties.data && imageData.properties.width) {
var pixelOffset = y * imageData.properties.width * 4 + x * 4;
imageData.properties.data.properties[pixelOffset] = r;
imageData.properties.data.properties[pixelOffset + 1] = g;
imageData.properties.data.properties[pixelOffset + 2] = b;
imageData.properties.data.properties[pixelOffset + 3] =
typeof a === 'undefined'
export function setRGB(imageData, x, y, r, g, b, a, calledWithinInterpreter) {
const imageDataProperties = calledWithinInterpreter
? imageData.properties
: imageData;
const imageDataDataValues = calledWithinInterpreter
? imageDataProperties.data.properties
: imageDataProperties.data;
if (imageDataProperties.data && imageDataProperties.width) {
var pixelOffset = y * imageDataProperties.width * 4 + x * 4;
imageDataDataValues[pixelOffset] = r;
imageDataDataValues[pixelOffset + 1] = g;
imageDataDataValues[pixelOffset + 2] = b;
if (typeof a === 'undefined') {
// In the interpreter, we must create an interpreter primitive
// to wrap the default value of 255:
imageDataDataValues[pixelOffset + 3] = calledWithinInterpreter
? window.Applab.JSInterpreter.createPrimitive(255)
: a;
: 255;
} else {
imageDataDataValues[pixelOffset + 3] = a;
}
}
}

const interpreterFunctions = {
insertItem: interpreterInsertItem,
removeItem: interpreterRemoveItem,
appendItem: interpreterAppendItem,
getRed: interpreterGetRed,
getGreen: interpreterGetGreen,
getBlue: interpreterGetBlue,
getAlpha: interpreterGetAlpha,
setRed: interpreterSetRed,
setGreen: interpreterSetGreen,
setBlue: interpreterSetBlue,
setAlpha: interpreterSetAlpha,
setRGB: interpreterSetRGB
};

export default interpreterFunctions;
2 changes: 1 addition & 1 deletion apps/src/applab/dropletConfig.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* global dashboard */
import $ from 'jquery';
import * as api from './api';
import * as dontMarshalApi from './dontMarshalApi';
import dontMarshalApi from './dontMarshalApi';
import consoleApi from '../consoleApi';
import * as audioApi from '@cdo/apps/lib/util/audioApi';
import audioApiDropletConfig from '@cdo/apps/lib/util/audioApiDropletConfig';
Expand Down
2 changes: 1 addition & 1 deletion apps/src/code-studio/components/SoundLibrary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const styles = {
float: 'left',
marginBottom: 20,
overflowY: 'scroll',
height: 350
height: 320
},
allCategoriesText: {
fontSize: 16,
Expand Down
2 changes: 1 addition & 1 deletion apps/src/code-studio/components/SoundList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import soundLibrary from '../soundLibrary.json';

const styles = {
root: {
height: 330,
height: 315,
overflowY: 'scroll',
clear: 'both'
}
Expand Down

0 comments on commit 03fce26

Please sign in to comment.