Share
Embed
JavaScript Code Analysis
San Francisco HTML5 User Group - http://www.sfhtml5.org/events/91184172/
Ariya Hidayat
December 05, 2012
Transcript
74 slides
-
JavaScript Code Analysis San Francisco December 5, 2012 Ariya Hidayat
http://www.sfhtml5.org @ariyahidayat 1 -
Do you... •have a coding style/guidelines? •use code quality tool
such as JSLint or JSHint? •actively prevent performance & coverage regressions? 2 -
Quality: Practical Aspects Avoid silly mistakes Write readable code Do
not provoke ambiguities Improve future maintenance Learn better code pattern 3 -
Tools and Mistakes Likelihood S = Skill C = Complexity
S C Average Engineer S C http://ariya.ofilabs.com/2012/11/language-tools-for-reducing-mistakes.html 4 -
Better Use of the Tools Engineer Tools Feedback Cycle Boring
Repetitive Time-consuming 6 -
Adaptive Tools Adjustment Explicit Implicit Customize analysis options Define new
sets of rules Infer from high-quality sample Observe the engineer’s behavior 7 -
From Spelling Checker to Grammar Enforcement Your so wrong, therefore
you loose! No misspelled word. Wrong choice of words! 8 -
Semantics + Convention var AppRouter = Backbone.Router.extend({ routes: { "posts/:id":
"getPost", "*actions": "defaultRoute" } }); var app_router = new AppRouter; app_router.on('route:getPos', function (id) { // Do Something... }); 9 -
JavaScript in the Browser User Interface Browser Engine Graphics Stack
Data Persistence Render Engine JavaScript Engine Networking I/O 11 -
JavaScript Engine Building Blocks Virtual Machine/ Interpreter Parser Runtime Source
Syntax Tree Built-in objects, host objects, ... Fast and conservative 12 -
Specification Conformance • ECMA-262 compliant • Automatic semicolon insertion •
Strict Mode, e.g. “use strict” • Unicode for identifiers 'use strict'; var ຊޠ = 1 return ຊޠ 15 -
{ type: "Program", body: [ { type: "ExpressionStatement", expression: {
type: "AssignmentExpression", operator: "=", left: { type: "Identifier", name: "answer" }, right: { type: "Literal", value: 42 } } } ] } Sensible Syntax Tree answer = 42 https://developer.mozilla.org/en/SpiderMonkey/Parser_API http://esprima.org/demo/parse.html Try online! 16 -
Specification, Parser Code, Syntax Tree function parseWhileStatement() { var test,
body; expectKeyword('while'); expect('('); test = parseExpression(); expect(')'); body = parseStatement(); return { type: 'WhileStatement', test: test, body: body }; } while ( Expression ) Statement ECMA-262 Annex A.4 17 -
Syntax Node Location { type: "ExpressionStatement", expression: { type: "AssignmentExpression",
operator: "=", left: { type: "Identifier", name: "answer", range: [0, 6] }, right: { type: "Literal", value: 42, range: [9, 11] }, range: [0, 11] }, range: [0, 11] } answer = 42 18 -
Error Tolerant Useful for IDE, editors, ... var msg =
"Hello’; person..age = 18; if (person. 'use strict'; with (person) { } Mismatched quote Too many dots Incomplete, still typing Strict mode violation 19 -
Handle the Comments https://github.com/thejohnfreeman/jfdoc Documentation tool Code annotation https://github.com/goatslacker/node-typedjs //
Life, Universe, and Everything answer = 42 comments: [ { range: [0, 34], type: "Line", value: " Life, Universe, and Everything" } ] 20 -
Forward Looking Experimental ‘harmony’ branch Object initializer shorthand { let
x; const y = 0; } Block scope var point = {x, y}; Module & class module LinearAlgebra { export const System = 'Cartesian'; } class Vector3 { constructor (x, y, z) { this.x = x; this.y = y; this.z = z; } } Destructuring assignment point = {14, 3, 77}; {x, y, z} = point; 21 -
Code Regeneration Esprima Escodegen Source Syntax Tree Syntax Transformation Shorten
variable name Inline short function Remove dead code Obfuscate Source 22 -
Linter vs Validator Validator Linter Looks for specification violations Does
not care about coding style Works well on generated/minified code Searches for suspicious pattern Warns on style inconsistencies Works well on hand-written code 30 -
(Git) Precommit Hook files=$(git diff-index --name-only HEAD | grep -P
'\.js$') for file in $files; do esvalidate $file if [ $? -eq 1 ]; then echo "Syntax error: $file" exit 1 fi done 31 -
Most Popular Keywords this function if return var else for
new in typeof while case break try catch delete throw switch continue default instanceof do void finally 4 10 12 14 15 25 35 38 72 84 84 115 122 143 188 225 232 436 562 2116 2878 3063 3108 3229 http://ariya.ofilabs.com/2012/03/most-popular-javascript-keywords.html var fs = require('fs'), esprima = require('esprima'), files = process.argv.splice(2); files.forEach(function (filename) { var content = fs.readFileSync(filename, 'utf-8'), tokens = esprima.parse(content, { tokens: true }).tokens; tokens.forEach(function (token) { if (token.type === 'Keyword') { console.log(token.value); } }); }); 36 -
Most Popular Statements http://ariya.ofilabs.com/2012/04/most-popular-javascript-statements.html ExpressionStatement BlockStatement IfStatement ReturnStatement VariableDeclaration FunctionDeclaration
ForStatement ForInStatement WhileStatement BreakStatement TryStatement EmptyStatement ThrowStatement SwitchStatement ContinueStatement DoWhileStatement LabeledStatement 6 12 25 35 38 66 84 115 131 143 293 371 2116 2878 3063 6353 6728 var fs = require('fs'), esprima = require('esprima'), files = process.argv.splice(2); files.forEach(function (filename) { var content = fs.readFileSync(filename, 'utf-8'), syntax = esprima.parse(content); JSON.stringify(syntax, function (key, value) { if (key === 'type') { if (value.match(/Declaration$/) || value.match(/Statement$/)) { console.log(value); } } return value; }); }); 37 -
Identifier Length Distribution http://ariya.ofilabs.com/2012/05/javascript-identifier-length-distribution.html 0 250 500 750 0 5
10 15 20 25 30 35 40 45 mean of the identifier length is 8.27 characters prototype-1.7.0.0.js SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING prototype-1.7.0.0.js MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED jquery-1.7.1.js subtractsBorderForOverflowNotVisible jquery.mobile-1.0.js getClosestElementWithVirtualBinding prototype-1.7.0.0.js HAS_EXTENDED_CREATE_ELEMENT_SYNTAX 38 -
More Code Metrics Comment density Expression depth Duplicated/similar fragment Native
objects/functions usage 39 -
“Code Linting” var fs = require('fs'), esprima = require('./esprima'), files
= process.argv.splice(2); files.forEach(function (filename) { var content = fs.readFileSync(filename, 'utf-8'), syntax = esprima.parse(content, { loc: true }); JSON.stringify(syntax, function (key, value) { if (key === 'test' && value.operator === '==') console.log('Line', value.loc.start.line); return value; }); }); if (x == 9) { // do Something } Not a strict equal 41 -
“Boolean Trap” Finder Can you make up your mind? treeItem.setState(true,
false); event.initKeyEvent("keypress", true, true, null, null, false, false, false, false, 9, 0); The more the merrier? Obfuscated choice var volumeSlider = new Slider(false); Double-negative component.setHidden(false); filter.setCaseInsensitive(false); http://ariya.ofilabs.com/2012/06/detecting-boolean-traps-with-esprima.html 42 -
Nested Ternary Conditionals http://ariya.ofilabs.com/2012/10/detecting-nested-ternary-conditionals.html var str = (age < 1)
? "baby" : (age < 5) ? "toddler" : (age < 18) ? "child": "adult"; 43 -
Strict Mode Check 'use strict'; block = { color: 'blue',
height: 20, width: 10, color: 'red' }; Duplicate data property in object literal not allowed in strict mode http://ariya.ofilabs.com/2012/10/validating-strict-mode.html 44 -
Polluting Variables var height; // some fancy processing heigth =
200; Leaks to global http://ariya.ofilabs.com/2012/11/polluting-and-unused-javascript-variables.html test.js:3 heigth = 200; ^ LeakError: global leak detected: heigth https://github.com/kesla/node-leaky 45 -
Unused Variables var height; // some fancy processing heigth =
200; Declared but not used http://ariya.ofilabs.com/2012/11/polluting-and-unused-javascript-variables.html test.js height - on line 1 https://github.com/Kami/node-unused 46 -
Code Complexity http://jscomplexity.org/ if (true) "foo"; else "bar"; ◦ Maintainability
index: 139.99732896539635 ◦ Physical LOC: 1 ◦ Logical LOC: 4 ◦ Aggregate cyclomatic complexity: 2 47 -
Statement Coverage http://ariya.ofilabs.com/2012/03/javascript-code-coverage-and-esprima.html x = 42; if (false) x =
-1; https://github.com/itay/node-cover 49 -
Instrumentation for Coverage http://itay.github.com/snug_codecoverage_slides/ var a = 5; { __statement_ZyyqFc(1);
var a = 5; } foo(); { __statement_ZyyqFc(2); __expression_kC$jur(3), foo(); } function foo() { ... }; function foo() { __block_n53cJc(1); ... } Statement Expression Block 50 -
Unit Test + Statement Coverage = Latent Trap http://ariya.ofilabs.com/2012/09/the-hidden-trap-of-code-coverage.html function
inc(p, q) { if (q == undefined) q = 1; return p + q/q; } assert("inc(4) must give 5", inc(4) == 5); function inc(p, q) { if (q == undefined) return p + 1; return p + q/q; } assert("inc(4) must give 5", inc(4) == 5); Does not catch the missing code sequence 51 -
Branch Coverage function inc(p, q) { if (q == undefined)
q = 1; return p + q/q; } assert("inc(4) must give 5", inc(4) == 5); E = Else is not taken https://github.com/yahoo/istanbul http://ariya.ofilabs.com/2012/12/javascript-code-coverage-with-istanbul.html 52 -
Execution Tracing http://ariya.ofilabs.com/2012/02/tracking-javascript-execution-during-startup.html https://gist.github.com/1823129 jQuery Mobile startup log 4640 function
calls jquery.js 26 jQuery jquery.js 103 init undefined, undefined, [object Object] jquery.js 274 each (Function) jquery.js 631 each [object Object], (Function), undefined jquery.js 495 isFunction [object Object] jquery.js 512 type [object Object] jquery.mobile.js 1857 [Anonymous] jquery.mobile.js 642 [Anonymous] jquery.mobile.js 624 enableMouseBindings jquery.mobile.js 620 disableTouchBindings 53 -
Tracking the Scalability http://ariya.ofilabs.com/2012/01/scalable-web-apps-the-complexity-issue.html Array.prototype.swap = function (i, j) {
var k = this[i]; this[i] = this[j]; this[j] = k; } Array.prototype.swap = function (i, j) { Log({ name: 'Array.prototype.swap', lineNumber: 1, range: [23, 94] }); var k = this[i]; this[i] = this[j]; this[j] = k; } 54 -
Non-Destructive Partial Source Modification Modified Intact Do not remove comments
Preserve indentation & other formatting Add “contextual” information Inject or change function invocation 56 -
String Literal Quotes http://ariya.ofilabs.com/2012/02/from-double-quotes-to-single-quotes.html console.log('Hello') [ { type: "Identifier", value:
"console", range: [0, 7] }, { type: "Punctuator", value: ".", range: [7, 8] }, { type: "Identifier", value: "log", range: [8, 11] }, { type: "Punctuator", value: "(", range: [11, 12] }, { type: "String", value: "\"Hello\"", range: [12, 19] }, { type: "Punctuator", value: ")", range: [19, 19] } ] console.log("Hello") May need proper escaping List of tokens 57 -
Style Formatter CodePainter Source Sample code Formatted source Infer coding
styles Indentation Quote for string literal Whitespace https://github.com/fawek/codepainter 58 -
Rewrite and Regenerate var syntax = esprima.parse('answer = 42;'); syntax.body[0].expression.right.value
= 1337; escodegen.generate(syntax) answer = 1337; answer = 42; https://github.com/Constellation/escodegen 59 -
Minification & Obfuscation Array.prototype.swap = function (first, second) { var
temp = this[first]; this[first] = this[second]; this[second] = temp; } Array.prototype.swap=function(a,b){var c=this[a];this[a]=this[b],this[b]=c} https://github.com/Constellation/esmangle 60 -
LLJS (Low-Level JavaScript) http://mbebenita.github.com/LLJS/ let x = 0; Block scope
let u8 flag; let i32 position; struct Point { int x, y; }; Data types let u16 *p = q; Pointers 62 -
Sweet.js for Macro macro def { case $name:ident $params $body
=> { function $name $params $body } } def sweet(a) { console.log(“Hello World”); } Define def.. ..so that you can write http://sweetjs.org/ 63 -
Transpilation: Class Harmony ES 5.1 // Vector in 3-D Cartesian
coordinate class Vector3 { constructor (x, y, z) { this.x = x; this.y = y; this.z = z; } } // Vector in 3-D Cartesian coordinate var Vector3 = (function () { function Vector3 (x, y, z) { this.x = x; this.y = y; this.z = z; } ; return Vector3;})(); Intact http://ariya.ofilabs.com/2012/09/javascripts-future-class-syntax.html 64 -
Transpilation: Module Harmony ES 5.1 http://ariya.ofilabs.com/2012/06/esprima-and-harmony-module.html module LinearAlgebra { //
Create 2-D point. export function Point(x, y) { return { x, y }; } } var LinearAlgebra = function() { // Create 2-D point. function Point(x, y) { return { x: x, y: y }; } return { Point: Point }; }(); Intact https://github.com/jdiamond/harmonizr 65 -
Assisted Code Review Should be automatic, based on predefined, historical,
or heuristic patterns 67 -
Syntax Query if (x = 0) { /* do Something
*/ } IfStatement.test AssigmentExpression[operator='='] Which syntax family should be the model? CSS selector? XPath? SQL? 68 -
Copy Paste (Mistake) Detection function inside(point, rect) { return (point.x
>= rect.x1) && (point.y >= rect.y1) && (point.x <= rect.x2) && (point.y <= rect.y1); } Forgotten change! 69 -
Refactoring Helper // Add two numbers function add(firt, two) {
return firt + two; } // Add two numbers function add(first, two) { return first + two; } 70 -
And Many More... Semantic Diff Symbolic execution Informative syntax error
Declarative transformation Pattern Matching 71 -
Parsing Infrastructure Smart editing Source transformation Minification & obfuscation Instrumentation
Code coverage Dependency analysis Documentation generator Conditional contracts 72 -
Next-Generation Code Quality Tools To boldly analyze what no man
has analyzed before... 73