DLint: dynamically checking bad coding practices in JavaScript (ISSTA'15 Slides)

47
Liang Gong 1 , Michael Pradel 2 , Manu Sridharan 3 and Koushik Sen 1 1 UC Berkeley 2 TU Darmstadt 3 Samsung Research America DLint: Dynamically Checking Bad Coding Practices in JavaScript

Transcript of DLint: dynamically checking bad coding practices in JavaScript (ISSTA'15 Slides)

Liang Gong1, Michael Pradel2, Manu Sridharan3 and Koushik Sen1

1 UC Berkeley 2 TU Darmstadt 3 Samsung Research America

DLint: Dynamically Checking Bad Coding Practices in JavaScript

Why JavaScript?

… 2

• The RedMonk Programming Language Ranking (1st) – Based on GitHub and StackOverflow

• Assembly language for the web • Web applications, DSL, Desktop apps, Mobile apps

3

• Designed and Implemented in 10 days • Not all decisions were well-thought • Problematic language features

– Error prone – Poor performance – Prone to security vulnerabilities

• Problematic features are still around – Backward compatibility

Problematic JavaScript

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

4

Problematic JavaScript

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

• Good coding practices • Informal rules • Improve code quality

• Better quality means: • Fewer correctness issues • Better performance • Better usability • Better maintainability • Fewer security loopholes • Fewer surprises • …

5

What are coding practices?

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

6

var sum = 0, value; var array = [11, 22, 33]; for (value in array) { sum += value; } > sum ?

Rule: avoid using for..in over arrays

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

7

var sum = 0, value; var array = [11, 22, 33]; for (value in array) { sum += value; } > sum ?

11 + 22 + 33 => 66 array index

(not array value) 0 + 1 + 2 => 3 array index : string 0+"0"+"1"+"2" => "0012"

Rule: avoid using for..in over arrays

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

• Cross-browser issues • Result depends on the Array prototype object

8

var sum = 0, value; var array = [11, 22, 33]; for (value in array) { sum += value; } > sum ?

11 + 22 + 33 => 66 array index

(not array value) 0 + 1 + 2 => 3 array index : string 0+"0"+"1"+"2" => "0012"

> "0012indexOftoString..."

Rule: avoid using for..in over arrays

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

9

var sum = 0, value; var array = [11, 22, 33]; for (value in array) { sum += value; } > sum ?

for (i=0; i < array.length; i++) { sum += array[i]; } function addup(element, index, array) { sum += element; } array.forEach(addup);

Rule: avoid using for..in over arrays

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

10

var sum = 0, value; var array = [11, 22, 33]; for (value in array) { sum += value; } > sum ?

for (i=0; i < array.length; i++) { sum += array[i]; } function addup(element, index, array) { sum += element; } array.forEach(addup);

Rule: avoid using for..in over arrays

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

Coding Practices and Lint Tools

• Existing Lint-like checkers – Inspect source code – Rule-based checking – Detect common mistakes

• Limitations: – Approximates behavior – Unknown aliases – Lint tools favor precision over soundness

• Difficulty: Precise static program analysis

11 Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

12

• Dynamic Linter checking code quality rules for JS • Open-source, robust, and extensible framework • Formalized and implemented 28 rules

– Counterparts of static rules – Additional rules

• Empirical study – Compare static and dynamic checking

DLint

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

a.f = b.g PutField(Read("a", a), "f", GetField(Read("b", b), "g")) if (a.f()) ... if (Branch(Method(Read("a", a), "f")()) ... x = y + 1 x = Write("x", Binary(‘+’,Read("y", y), Literal(1))

analysis.Literal(c) analysis.Branch(c) analysis.Read(n, x) analysis.Write(n, x) analysis.PutField(b, f, v) analysis.Binary(op, x, y) analysis.Function(f, isConstructor) analysis.GetField(b,f) analysis.Method(b, f, isConstructor) analysis.Unary(op, x) ...

13

Jalangi: A Dynamic Analysis Framework for JavaScript [Sen et al. ESEC/FSE'2013]

• Single-event: Stateless checking • Multi-event: Stateful checking

14

Runtime Patterns

• propWrite(base, name, val) • propRead(base, name, val) • cond(val) • unOp(op, val, res) • binOp(op, left, right, res) • call(base, f, args, ret, isConstr)

Predicates over runtime events

Example: propWrite(*, "myObject", val) | isPrim(val)

Formalization: declarative specification

15

16

Language Misuse

Avoid setting properties of primitives, which has no effect. var fact = 42; fact.isTheAnswer = true; console.log(fact.isTheAnswer); > undefined DLint Checker Predicate: propWrite(base,*,*) Λ isPrim(base)

17

Avoid producing NaN (Not a Number). var x = 23 – "five"; > NaN DLint Checker Predicate: unOp(*, val, NaN) Λ val ≠ NaN

binOp(*, left, right, NaN) Λ left ≠ NaN

Λ right ≠ NaN

call(*, *, args,NaN, *) Λ NaN ≠ args

Uncommon Values

18

Avoid concatenating undefined to string. var value; ... var str = "price: "; ... var result = str + value; > "price: undefined" DLint Checker Predicate: binOp(+, left, right, res) Λ (left = undefined ∨ right = undefined) Λ isString(res)

Uncommon Values

19

Beware that all wrapped primitives coerce to true. var b = false; if (new Boolean(b)) { console.log("true"); } > true

DLint Checker Predicate: cond(val) Λ isWrappedBoolean(val) Λ val.valueOf() = false

API Misuse

Avoid accessing the undefined property.

var x; // undefined var y = {}; y[x] = 23; // { undefined: 23 }

propWrite(*, "undefined",*) propRead(*, "undefined", *)

20

Type Related Checker

21

22

23

• Modify SpiderMonkey to intercept JavaScript files • Transpile JavaScript code with Jalangi • DLint monitors runtime states and finds issues • Reports root cause and code location

24

JS program Instrumented JS program

Behavior Information Final Report

DLint Checkers

Jalangi

DLint Overview

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

25

Evaluation

Research Questions • DLint warnings vs. JSHint warnings? • Additional warnings from DLint? • Coding convention vs. page popularity?

Experimental Setup • 200 web sites (top 50 + others) • Comparison to JSHint

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

26

• Some sites: One approach finds all • Most sites: Better together

0% 20% 40% 60% 80% 100%

% of warnings on each site

Web

sites

JSHint Unique Common DLint Unique

% of Warnings: DLint vs. JSHint

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

27

• 53 warnings per page • 49 are missed by JSHint

0

1

2

3

4

I5 T6 L3 T5 A2 V2 L4 A5 T1 L2 L1 A6 A8 A3 T2 A4 I1 I4 V3 L5 I2 T4 L6 A7 T3Loga

rithm

ic a

vera

ge. #

w

arni

ng /

site

(bas

e 2)

DLint checkers

8

4

2

1

0

Checker shares warning with JSHint Checker shares no warnings with JSHint

Additional Warnings Reported by DLint

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

28

Correlation between Alexa popularity and number of DLint warnings: 0.6

00.0025

0.0050.0075

0.010.0125

0.0150.0175

0.02

# DL

int W

arn.

/ #C

ov. O

p.

Websites traffic ranking

Quartile 1-3

Mean

00.020.040.060.08

0.10.120.140.16

# JS

Hint

War

ning

/ LO

C

Websites traffic ranking

Quartile 1-3

Mean

Coding Convention vs. Page Popularity

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

29

74

359

191

416

1401

62

202

2335

4933

79

205

62

125

167

6

15

26 8

0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%

Do not use Number, Boolean, String as a constructor.

The Function constructor is a form of eval.

The object literal notation {} is preferable.

The array literal notation [] is preferable.

document.write can be a form of eval.

Do not override built-in variables.

Implied eval (string instead of function as argument).

eval can be harmful.

Missing 'new' prefix when invoking a constructor.

Fount by JSHint Only Common with DLint

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

30

E.g., 181 calls of eval(), Function(), etc. missed by JSHint

31

77

181

74

833

79

187

413

6

8

0% 20% 40% 60% 80% 100%

WrappedPrimitives

Literals

DoubleEvaluation

ArgumentsVariable

ConstructorFunctions

A8

L4A

1L1

T5

Found by Dlint Only Common with JSHint

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

31 Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

32 Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

33 Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

34 Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

NaN case study for IKEA

35

<prices> <normal> <priceNormal unformatted="9.9">$9.90</priceNormal> <pricePrevious /> <priceNormalPerUnit /> <pricePreviousPerUnit /> </normal> </prices>

previousPrice = getElementValue("pricePrevious" + suffix, normal);

parseFloat(previousPrice).toFixed(2).replace(...) .replace('.00', ''));

XML: Empty Element

JS: undefined

JS: NaN

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley. 36

37 Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

38 Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

39 Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

40

var decode64 = ""; if (dataLength > 3 && dataLength % 4 == 0) { while (index < dataLength) { decode64 += String.fromCharCode(...); } if (sixbits[3] >= 0x40) { decode64.length -= 1; } }

From Google Octane’s Game Boy Emulator benchmark:

Rule: avoid setting field on primitive values

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

41

var decode64 = ""; if (dataLength > 3 && dataLength % 4 == 0) { while (index < dataLength) { decode64 += String.fromCharCode(...); } if (sixbits[3] >= 0x40) { decode64.length -= 1; } }

From Google Octane’s Game Boy Emulator benchmark:

No effect because decode64 is a primitive string.

Rule: avoid setting field on primitive values

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

42

window.onbeforeunload= "Twitch.player.getPlayer().pauseVideo();" window.onunload= "Twitch.player.getPlayer().pauseVideo();"

Rule: avoid no effect operations

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

43

window.onbeforeunload= "Twitch.player.getPlayer().pauseVideo();" window.onunload= "Twitch.player.getPlayer().pauseVideo();"

window.onbeforeunload = function () { Twitch.player.getPlayer().pauseVideo(); }

Rule: avoid no effect operations

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

www.google.com/chrome, included code from Modernizr:

https://github.com/Modernizr/Modernizr/pull/1419

44

Rule: avoid using for..in on arrays

for (i in props) { // props is an array prop = props[i]; before = mStyle.style[prop]; ... }

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

45

Takeaways

Dynamic lint-like checking for JavaScript • Static checkers are not sufficient, DLint complements • DLint is an open-source, robust, and extensible tool

– Works on real-world websites – Found 19 clear bugs on most popular websites

More information: • Paper: “DLint: Dynamically Checking Bad Coding Practices in JavaScript” • Source Code: https://github.com/Berkeley-Correctness-Group/DLint • Google “DLint Berkeley”

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

46

Takeaways

Thanks!

Dynamic lint-like checking for JavaScript • Static checkers are not sufficient, DLint complements • DLint is an open-source, robust, and extensible tool

– Works on real-world websites – Found 19 clear bugs on most popular websites

More information: • Paper: “DLint: Dynamically Checking Bad Coding Practices in JavaScript” • Source Code: https://github.com/Berkeley-Correctness-Group/DLint • Google “DLint Berkeley”

Liang Gong, Electric Engineering & Computer Science, University of California, Berkeley.

47

http

://w

ww

.phd

com

ics.

com

/com

ics.

php?

f=15

53