Comparing version 0.1.16 to 0.1.17
@@ -5,2 +5,5 @@ var U = require('../../utils'), | ||
fs = require('fs'), | ||
wrench = require('wrench'), | ||
jsp = require("../../uglify-js/uglify-js").parser, | ||
pro = require("../../uglify-js/uglify-js").uglify, | ||
_ = require('../../lib/alloy/underscore')._; | ||
@@ -12,3 +15,4 @@ | ||
JSON_NULL = JSON.parse('null'), | ||
stylePrefix = '\t\t'; | ||
stylePrefix = '\t\t', | ||
compilerConfig; | ||
@@ -46,4 +50,6 @@ var NS_TI_MAP = 'Ti.Map', | ||
exports.STYLE_ALLOY_TYPE = '__ALLOY_TYPE__'; | ||
exports.STYLE_CONST_PREFIX = '__ALLOY_CONST__:'; | ||
var STYLE_ALLOY_TYPE = '__ALLOY_TYPE__'; | ||
var STYLE_CONST_PREFIX = '__ALLOY_CONST__--'; | ||
var STYLE_EXPR_PREFIX = '__ALLOY_EXPR__--'; | ||
var PLATFORMS = ['ios', 'android', 'mobileweb']; | ||
@@ -66,3 +72,5 @@ ////////////////////////////////////// | ||
req = node.getAttribute('require'), | ||
id = node.getAttribute('id') || state.defaultId || req || exports.generateUniqueId(); | ||
id = node.getAttribute('id') || state.defaultId || req || exports.generateUniqueId(), | ||
platform = node.getAttribute('platform'), | ||
platformObj = {}; | ||
@@ -72,2 +80,24 @@ ns = ns.replace(/^Titanium\./, 'Ti'); | ||
if (state.defaultId) { delete state.defaultId; } | ||
// process the platform attribute | ||
if (platform) { | ||
_.each((platform).split(','), function(p) { | ||
var matches = U.trim(p).match(/^(\!{0,1})(.+)/); | ||
if (matches !== null) { | ||
var negate = matches[1]; | ||
var name = matches[2]; | ||
if (_.contains(PLATFORMS, name)) { | ||
if (negate === '!') { | ||
_.each(_.without(PLATFORMS, name), function(n) { | ||
platformObj[n] = true; | ||
}); | ||
} else { | ||
platformObj[name] = true; | ||
} | ||
return; | ||
} | ||
} | ||
U.die('Invalid platform type found: ' + p); | ||
}); | ||
} | ||
@@ -82,5 +112,84 @@ return { | ||
parent: state.parent || {}, | ||
platform: !platformObj ? undefined : platformObj | ||
}; | ||
}; | ||
var conditionMap = { | ||
android: { | ||
compile: 'OS_ANDROID', | ||
runtime: "TI.Platform.osname === 'android'" | ||
}, | ||
ios: { | ||
compile: 'OS_IOS', | ||
runtime: "Ti.Platform.osname === 'ipad' || Ti.Platform.osname === 'iphone'" | ||
}, | ||
mobileweb: { | ||
compile: 'OS_MOBILEWEB', | ||
runtime: "Ti.Platform.osname === 'mobileweb'" | ||
} | ||
} | ||
exports.generateNode = function(node, state, defaultId, isRoot) { | ||
if (node.nodeType != 1) return ''; | ||
if (defaultId) { state.defaultId = defaultId; } | ||
var args = exports.getParserArgs(node, state, defaultId), | ||
codeTemplate = "if (<%= condition %>) {\n<%= content %>}\n", | ||
code = { content: '' }; | ||
// Check for platform-specific considerations | ||
var conditionType = compilerConfig && compilerConfig.alloyConfig && compilerConfig.alloyConfig.platform ? 'compile' : 'runtime'; | ||
if (args.platform) { | ||
var conditionArray = []; | ||
_.each(args.platform, function(v,k) { | ||
conditionArray.push(conditionMap[k][conditionType]); | ||
}); | ||
code.condition = conditionArray.join(' || '); | ||
} | ||
// Determine which parser to use for this node | ||
var parsersDir = path.join(alloyRoot,'commands','compile','parsers'); | ||
var parserRequire = 'default'; | ||
if (_.contains(fs.readdirSync(parsersDir), args.fullname+'.js')) { | ||
parserRequire = args.fullname+'.js'; | ||
} | ||
// Execute the appropriate tag parser and append code | ||
state = require('./parsers/' + parserRequire).parse(node, state) || { parent: {} }; | ||
code.content += state.code; | ||
if (isRoot) { code.content += 'root$ = ' + args.symbol + ';\n'; } | ||
// Continue parsing if necessary | ||
if (state.parent) { | ||
var states = _.isArray(state.parent) ? state.parent : [state.parent]; | ||
_.each(states, function(p) { | ||
var newParent = p.node; | ||
for (var i = 0, l = newParent.childNodes.length; i < l; i++) { | ||
code.content += exports.generateNode(newParent.childNodes.item(i), { | ||
parent: p, | ||
styles: state.styles, | ||
}); | ||
} | ||
}); | ||
} | ||
return code.condition ? _.template(codeTemplate, code) : code.content; | ||
} | ||
exports.copyWidgetAssets = function(assetsDir, resourceDir, widgetId) { | ||
if (!path.existsSync(assetsDir)) { return; } | ||
var files = wrench.readdirSyncRecursive(assetsDir); | ||
_.each(files, function(file) { | ||
var source = path.join(assetsDir, file); | ||
if (fs.statSync(source).isFile()) { | ||
var destDir = path.join(resourceDir, path.dirname(file), widgetId); | ||
var dest = path.join(destDir, path.basename(file)); | ||
if (!path.existsSync(destDir)) { | ||
wrench.mkdirSyncRecursive(destDir, 0777); | ||
} | ||
console.log('Copying assets ' + source + ' --> ' + dest); | ||
U.copyFileSync(source, dest); | ||
} | ||
}); | ||
} | ||
// "Empty" states are generally used when you want to create a | ||
@@ -129,2 +238,5 @@ // Titanium component with no parent | ||
// keep a copy of the config for this module | ||
compilerConfig = obj; | ||
return obj; | ||
@@ -161,14 +273,63 @@ }; | ||
// use Ti namespace | ||
f = f.replace(/Titanium\./g,"Ti."); | ||
// Handle "call" ASTs, where we look for expr() syntax | ||
function do_call() { | ||
if (this[1][1] === 'expr') { | ||
var code = pro.gen_code(this[2][0]); | ||
var new_ast = ['string', STYLE_CONST_PREFIX + code]; | ||
return new_ast; | ||
} | ||
}; | ||
// find constants | ||
// TODO: This needs work. There's still an off chance that this could | ||
// match content in a string. Or that the STYLE_CONST_PREFIX could | ||
// appear in other style strings. Extremely unlikely, but possible. | ||
f = f.replace(/\:\s*(Ti\.[^\s\,\}]+)/g, ': "' + exports.STYLE_CONST_PREFIX + '$1"'); | ||
// Recursively assemble the full name of a dot-notation variable | ||
function processDot(dot,name) { | ||
switch(dot[0]) { | ||
case 'dot': | ||
return processDot(dot[1], '.' + (dot[2] || '') + name); | ||
break; | ||
case 'name': | ||
var pre = dot[1]; | ||
if (pre === 'Ti' || pre === 'Titanium') { | ||
return pre + name; | ||
} else { | ||
return null; | ||
} | ||
break; | ||
} | ||
} | ||
// Handle all AST "dot"s, looking for Titanium constants | ||
function do_dot() { | ||
var name = processDot(this,''); | ||
if (name === null) { | ||
return null; | ||
} else { | ||
return ['string', STYLE_CONST_PREFIX + name]; | ||
} | ||
} | ||
// Generate AST and add the handlers for "call" and "dot" to the AST walker | ||
var ast = jsp.parse('module.exports = ' + f); | ||
//console.log(require('util').inspect(ast, false, null)); | ||
var walker = pro.ast_walker(); | ||
var new_ast = walker.with_walkers({ | ||
"call": do_call, | ||
"dot": do_dot | ||
}, function(){ | ||
return walker.walk(ast); | ||
}); | ||
// generate code based on the new AST. Make sure to keep keys quoted so the | ||
// JSON parses without exception. The wild [1][0][1][3] array is how we grab | ||
// just the style object from the AST, leaving behind the appended "module.exports = " | ||
var code = pro.gen_code(new_ast[1][0][1][3], { | ||
beautify: true, | ||
quote_keys: true, | ||
keep_zeroes: true, | ||
double_quotes: true | ||
}); | ||
try { | ||
return JSON.parse(f); | ||
return JSON.parse(code); | ||
} catch(E) { | ||
console.error(code); | ||
U.die("Error parsing style at "+p.yellow+". Error was: "+String(E).red); | ||
@@ -181,15 +342,13 @@ } | ||
exports.createVariableStyle = function(keyValuePairs, value) { | ||
var style = {}, | ||
key, value; | ||
if (_.isArray(keyValuePairs)) { | ||
_.each(keyValuePairs, function(pair) { | ||
var k = pair[0]; | ||
var v = pair[1]; | ||
style[k] = { value:v }; | ||
style[k][exports.STYLE_ALLOY_TYPE] = 'var'; | ||
}); | ||
} else { | ||
style[keyValuePairs] = { value:value }; | ||
style[keyValuePairs][exports.STYLE_ALLOY_TYPE] = 'var'; | ||
var style = {}; | ||
if (!_.isArray(keyValuePairs)) { | ||
keyValuePairs = [[keyValuePairs, value]]; | ||
} | ||
_.each(keyValuePairs, function(pair) { | ||
var k = pair[0]; | ||
var v = pair[1]; | ||
style[k] = { value:v }; | ||
style[k][STYLE_ALLOY_TYPE] = 'var'; | ||
}); | ||
return style; | ||
@@ -231,3 +390,3 @@ }; | ||
var regex = new RegExp('^' + exports.STYLE_CONST_PREFIX + '(.+)'); | ||
var regex = new RegExp('^' + STYLE_CONST_PREFIX + '(.+)'); | ||
for (var sn in s) { | ||
@@ -244,3 +403,3 @@ var value = s[sn], | ||
} | ||
} else if (_.isObject(value) && value[exports.STYLE_ALLOY_TYPE] === 'var') { | ||
} else if (_.isObject(value) && value[STYLE_ALLOY_TYPE] === 'var') { | ||
actualValue = value.value; // dynamic variable value | ||
@@ -247,0 +406,0 @@ } else { |
@@ -162,3 +162,7 @@ var path = require('path'), | ||
for (var i = 0, l = docRoot.childNodes.length; i < l; i++) { | ||
template.viewCode += generateNode(docRoot.childNodes.item(i),state,viewid||viewname); | ||
template.viewCode += CU.generateNode( | ||
docRoot.childNodes.item(i), | ||
state, | ||
viewid||viewname, | ||
true); | ||
} | ||
@@ -172,2 +176,3 @@ template.controllerCode += generateController(viewName,dir,id); | ||
wrench.mkdirSyncRecursive(path.join(compileConfig.dir.resourcesAlloy, 'widgets', manifest.id, 'components'), 0777); | ||
CU.copyWidgetAssets(path.join(dir,'assets'), compileConfig.dir.resources, manifest.id); | ||
fs.writeFileSync(path.join(compileConfig.dir.resourcesAlloy, 'widgets', manifest.id, 'components', viewName + '.js'), code); | ||
@@ -179,41 +184,2 @@ } else { | ||
function generateNode(node, state, defaultId) { | ||
if (node.nodeType != 1) return ''; | ||
if (defaultId) { state.defaultId = defaultId; } | ||
var args = CU.getParserArgs(node, state, defaultId), | ||
code = ''; | ||
// Determine which parser to use for this node | ||
var parsersDir = path.join(alloyRoot,'commands','compile','parsers'); | ||
var files = fs.readdirSync(parsersDir); | ||
var parserRequire = 'default'; | ||
for (var i = 0, l = files.length; i < l; i++) { | ||
if (args.fullname + '.js' === files[i]) { | ||
parserRequire = files[i]; | ||
break; | ||
} | ||
} | ||
// Execute the appropriate tag parser and append code | ||
state = require('./parsers/' + parserRequire).parse(node, state) || { parent: {} }; | ||
code += state.code; | ||
// Continue parsing if necessary | ||
if (state.parent) { | ||
var states = _.isArray(state.parent) ? state.parent : [state.parent]; | ||
_.each(states, function(p) { | ||
var newParent = p.node; | ||
for (var i = 0, l = newParent.childNodes.length; i < l; i++) { | ||
code += generateNode(newParent.childNodes.item(i), { | ||
parent: p, | ||
styles: state.styles, | ||
}); | ||
} | ||
}); | ||
} | ||
return code; | ||
} | ||
function generateController(name, dir, id) { | ||
@@ -220,0 +186,0 @@ var controllerDir = dir ? path.join(dir,'controllers') : compileConfig.dir.controllers, |
@@ -15,5 +15,3 @@ var CU = require('../compilerUtils'); | ||
code += linePrefix + args.parent.symbol + ".add(" + args.symbol + ");\n"; | ||
} else { | ||
code += linePrefix + "root$ = " + args.symbol + ";\n"; | ||
} | ||
} | ||
@@ -20,0 +18,0 @@ // Update the parsing state |
@@ -38,4 +38,3 @@ // TODO: pass errors back to the calling function in the compile | ||
if (annotationSymbols.length > 0) { | ||
var extraStyle = { annotations: { value:'[' + annotationSymbols.join(',') + ']' } }; | ||
extraStyle.annotations[CU.STYLE_ALLOY_TYPE] = 'var'; | ||
var extraStyle = CU.createVariableStyle('annotations', '[' + annotationSymbols.join(',') + ']'); | ||
mapState = require('./default').parse(node, state, extraStyle); | ||
@@ -42,0 +41,0 @@ } else { |
@@ -43,9 +43,4 @@ // TODO: pass errors back to the calling function in the compile | ||
// Generate the code for the Tab itself, with the Window in it | ||
var extraStyle = { window: { value: winState.parent.symbol } }; | ||
extraStyle.window[CU.STYLE_ALLOY_TYPE] = 'var'; | ||
var tabState = require('./default').parse( | ||
node, | ||
CU.createEmptyState(state.styles), | ||
extraStyle | ||
); | ||
var extraStyle = CU.createVariableStyle('window', winState.parent.symbol); | ||
var tabState = require('./default').parse(node, CU.createEmptyState(state.styles), extraStyle); | ||
code += tabState.code; | ||
@@ -52,0 +47,0 @@ |
@@ -17,5 +17,3 @@ var CU = require('../compilerUtils'); | ||
code += args.symbol + '.setParent(' + args.parent.symbol + ');\n'; | ||
} else { | ||
code += "root$ = " + args.symbol + ";\n"; | ||
} | ||
} | ||
@@ -26,3 +24,3 @@ // Update the parsing state | ||
node: node, | ||
symbol: args.symbol | ||
symbol: args.symbol + '.getRoot()' | ||
}, | ||
@@ -29,0 +27,0 @@ styles: state.styles, |
@@ -18,5 +18,3 @@ var CU = require('../compilerUtils'), | ||
code += args.symbol + '.setParent(' + args.parent.symbol + ');\n'; | ||
} else { | ||
code += "root$ = " + args.symbol + ";\n"; | ||
} | ||
} | ||
@@ -27,3 +25,3 @@ // Update the parsing state | ||
node: node, | ||
symbol: args.symbol | ||
symbol: args.symbol + '.getRoot()' | ||
}, | ||
@@ -30,0 +28,0 @@ styles: state.styles, |
@@ -5,3 +5,3 @@ var _ = require("../../lib/alloy/underscore")._, | ||
// TODO: generate TARGETS array by reading the Alloy/commands/generate files | ||
var TARGETS = ['controller', 'view', 'model', 'migration', 'widget']; | ||
var TARGETS = ['controller', 'view', 'model', 'migration', 'vc', 'styles', 'widget']; | ||
@@ -8,0 +8,0 @@ function generate(args, program) { |
var _ = require("alloy/underscore")._, | ||
Backbone = require("alloy/backbone"), | ||
SQLSync = require("alloy/sync/sql"), | ||
FileSysSync = require("alloy/sync/filesys"), | ||
osname = Ti.Platform.osname; | ||
osname = Ti.Platform.osname, | ||
SQLSync, | ||
SQLSyncInit, | ||
FileSysSync; | ||
@@ -22,2 +23,3 @@ module.exports._ = _; | ||
case 'sql': { | ||
SQLSync = require("alloy/sync/sql"); | ||
SQLSync.sync(model,method,opts); | ||
@@ -27,2 +29,3 @@ break; | ||
case 'filesystem': { | ||
FileSysSync = require("alloy/sync/filesys"); | ||
FileSysSync.sync(model,method,opts); | ||
@@ -43,3 +46,6 @@ break; | ||
var type = (config.adapter ? config.adapter.type : null) || 'sql'; | ||
if (type == 'sql') { SQLSync.init(); } | ||
if (type === 'sql' && !SQLSyncInit) { | ||
SQLSyncInit = true; | ||
SQLSync.init(); | ||
} | ||
@@ -50,4 +56,3 @@ var Model = Backbone.Model.extend( { | ||
validate: function(attrs) | ||
{ | ||
validate: function(attrs) { | ||
if (typeof __validate !== 'undefined') { | ||
@@ -66,6 +71,6 @@ if (_.isFunction(__validate)) { | ||
if (migrations && migrations.length > 0) | ||
{ | ||
SQLSync.migrate(migrations); | ||
if (migrations && migrations.length > 0) { | ||
if (type == 'sql') { | ||
SQLSync.migrate(migrations); | ||
} | ||
} | ||
@@ -80,10 +85,7 @@ | ||
module.exports.A = function(t,type,parent) | ||
{ | ||
_.extend(t,{nodeType:1, nodeName:type, parentNode: parent}); | ||
module.exports.A = function(t,type,parent) { | ||
_.extend(t,Backbone.Events); | ||
(function(){ | ||
(function() { | ||
// we are going to wrap addEventListener and removeEventListener | ||
@@ -99,12 +101,8 @@ // with on, off so we can use the Backbone events | ||
t.on = function(e,cb,context) | ||
{ | ||
var wcb = function(evt) | ||
{ | ||
try | ||
{ | ||
t.on = function(e,cb,context) { | ||
var wcb = function(evt) { | ||
try { | ||
_.bind(tg,ctx,e,evt)(); | ||
} | ||
catch(E) | ||
{ | ||
catch(E) { | ||
Ti.API.error("Error triggering '"+e+"' event: "+E); | ||
@@ -124,7 +122,5 @@ } | ||
t.off = function(e,cb,context) | ||
{ | ||
t.off = function(e,cb,context) { | ||
var f = cbs[cb]; | ||
if (f) | ||
{ | ||
if (f) { | ||
_.bind(of,ctx,e,cb,context)(); | ||
@@ -131,0 +127,0 @@ |
@@ -46,2 +46,5 @@ // TODO: Optimize out lifecycle events if they are not defined | ||
} | ||
}, | ||
getRoot: function() { | ||
return root$; | ||
} | ||
@@ -48,0 +51,0 @@ }; |
@@ -172,3 +172,3 @@ // The island of misfit toys... for functions | ||
exports.ensureDir(rd); | ||
wrench.copyDirSyncRecursive(fpath, rd); | ||
wrench.copyDirSyncRecursive(fpath, rd, {preserve:true}); | ||
} | ||
@@ -242,11 +242,12 @@ else | ||
var isDev = config.deploytype === 'development'; | ||
var options = | ||
{ | ||
ast: false, | ||
consolidate: true, | ||
mangle: true, | ||
consolidate: !isDev, | ||
mangle: !isDev, | ||
mangle_toplevel: false, | ||
no_mangle_functions: false, | ||
squeeze: true, | ||
make_seqs: true, | ||
squeeze: !isDev, | ||
make_seqs: !isDev, | ||
dead_code: true, | ||
@@ -253,0 +254,0 @@ unsafe: false, |
@@ -16,3 +16,3 @@ { | ||
], | ||
"version": "0.1.16", | ||
"version": "0.1.17", | ||
"author": "Appcelerator, Inc. <info@appcelerator.com>", | ||
@@ -19,0 +19,0 @@ "maintainers": [ |
@@ -334,4 +334,2 @@ Alloy | ||
_NOTE: it's import that you use unique `id` attributes across all your view files. If you don't, you will get a compiler error._ | ||
Exporting Properties & Functions from Controllers | ||
@@ -380,3 +378,3 @@ ------------------------------------------------- | ||
If your widget would like to export properties and/or functions, it should assign them to the `exports` variable of the `widget.js`. | ||
If your widget would like to export properties and/or functions, it should assign them to the `$` variable of the `widget.js`. | ||
@@ -404,2 +402,45 @@ In your app controller, you would then be able to access them referencing the widget reference and the name of the property. | ||
Widget Assets | ||
------------- | ||
Widgets are to be self-contained components that can be easily dropped into Alloy-powered Titanium projects. For this reason, widgets can have their own collection of assets. These assets will be intelligently overlayed on your project's `Resources` directory at compile time. In this way, you can still specify platform-specific assets, yet keep these assets unique to your widget id. | ||
For example, the following `app` folder structure for an alloy project: | ||
``` | ||
app | ||
- widgets | ||
- com.appc.testwidget | ||
- assets | ||
- iphone | ||
- images | ||
- myimage.png | ||
- myimage@2x.png | ||
``` | ||
would be copied to your generated Titanium project as: | ||
``` | ||
Resources | ||
- iphone | ||
- images | ||
- com.appc.testwidget | ||
- myimage.png | ||
- myimage@2x.png | ||
``` | ||
and those files could then be accessed in your widget's code and styles as: | ||
```javascript | ||
var image = Ti.UI.createImageView({ | ||
image: '/images/com.appc.testwidget/myimage.png' | ||
}); | ||
``` | ||
This example shows only `iphone`, but widget assets can be copied to any path in the Resources directory. The final generated path will always have the widget's id as a folder, just before the file name. Here's a few more examples for clarity: | ||
* `app/widgets/com.appc.mywidget/assets/images/cool.png` --> `Resources/images/com.appc.mywidget/cool.png` | ||
* `app/widgets/com.appc.widget/images/android/images/res-hdpi/highresimage.png` --> `Resources/android/images/res-hdpi/com.appc.widget/highresimage.png` | ||
* `app/widgets/com.appc.lastone/some/weird/path/file.txt` --> `Resources/some/weird/path/com.appc.lastone/file.txt` | ||
Builtin JS LIbraries | ||
@@ -406,0 +447,0 @@ -------------------- |
@@ -13,3 +13,3 @@ /// Function to keep a Ti.TableView in sync with Backbone CRUD opperations | ||
// CRUD ops handler, put any special model processing here, some are ignored for this sample | ||
var CRUDEops = { | ||
var CRUDops = { | ||
"create": function(o) { Ti.API.info("create called with model="+JSON.stringify(o)); }, | ||
@@ -23,3 +23,3 @@ "read": function(o) { $.table.updateContent(o); }, | ||
$.BookCollection.notify.on('sync', function(e) { | ||
CRUDEops[e.method](e.model); | ||
CRUDops[e.method](e.model); | ||
}); | ||
@@ -26,0 +26,0 @@ |
{ | ||
"Button" : { | ||
"width" : Ti.UI.SIZE, | ||
"width" : expr(Ti.Platform.displayCaps.platformWidth/2), | ||
"height" : Ti.UI.SIZE | ||
@@ -5,0 +5,0 @@ }, |
@@ -13,3 +13,3 @@ // Function to keep a Ti.TableView in sync with Backbone CRUD opperations | ||
// CRUD ops handler, put any special model processing here, some are ignored for this sample | ||
var CRUDEops = { | ||
var CRUDops = { | ||
"create": function(o) { Ti.API.info("create called with model="+JSON.stringify(o)); }, | ||
@@ -23,3 +23,3 @@ "read": function(o) { $.table.updateContent(o); }, | ||
$.BookCollection.notify.on('sync', function(e) { | ||
CRUDEops[e.method](e.model); | ||
CRUDops[e.method](e.model); | ||
}); | ||
@@ -26,0 +26,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
937273
282
15552
646
34