ko.utils(配列編)

これはKnockoutJSアドベントカレンダーの記事です。


KnockoutJS Advent Calendar 2014 - Qiita


今日は Knockout の便利機能が入った ko.utils について適当に紹介します。

今回は配列編。

紹介するのは以下のメソッドです。

  • ko.utils.arrayForEach
  • ko.utils.arrayFirst
  • ko.utils.arrayFilter
  • ko.utils.arrayGetDistinctValues
  • ko.utils.arrayMap
  • ko.utils.arrayPushAll
  • ko.utils.arrayRemoveItem

ちなみにソースコードはここです。

https://github.com/knockout/knockout/blob/master/src/utils.js


  • ko.utils.arrayForEach

配列をぐるぐるします。

サンプル

var years = [1950, 1960, 1970, 1980];
  ko.utils.arrayForEach(years, function(item, index){
    console.log(index + " : " + item);
});

結果

0 : 1950
1 : 1960
2 : 1970
3 : 1980
  • ko.utils.arrayFirst

配列の中で最初に条件に一致した値を返します。

サンプル

var strArray = ["a1", "b2", "c3", "d4"];
var result = ko.utils.arrayFirst(strArray, function(value){ return value.charAt(0) === "b"});
console.log(result);

結果

b2
  • ko.utils.arrayFilter

配列を指定した条件でフィルタした結果の配列を返します。

サンプル

var result = ko.utils.arrayFilter(["a", "b", "c", "d", "e", "f"], function (item, index) {
  return index % 2 == 0;
});
console.log(result);

結果

["a", "c", "e"]
  • ko.utils.arrayGetDistinctValues

配列の中の重複しない値を返します。

var result = ko.utils.arrayGetDistinctValues(["a", "b", "b", "c", "c"]);
console.log(result);

結果

["a", "b", "c"]
  • ko.utils.arrayIndexOf

最初に見つかった配列の値のインデックスを返します。
見つからなかった場合は -1 を返します。

var result1 = ko.utils.arrayIndexOf(["a", "b", "c", "d", "c"], "c");
console.log(result1);
var result2 = ko.utils.arrayIndexOf(["a", "b", "c"], "d");
console.log(result2);

結果

2
-1
  • ko.utils.arrayMap

配列の中の各要素に対して特定の処理を行った配列を返します。

var result = ko.utils.arrayMap([1, 2, 3, 4, 5], function(item, index){
  return item * 2;
});
console.log(result);

結果

[2, 4, 6, 8, 10]
  • ko.utils.arrayPushAll

配列に他の配列を追加します。

サンプル

var targetArray = [1,2,3];
var extraArray = ["a", "b", "c"];
ko.utils.arrayPushAll(targetArray, extraArray);
console.log(targetArray);

結果

[1, 2, 3, "a", "b", "c"]
  • ko.utils.arrayRemoveItem

最初に一致した値を配列から削除します。(一つだけ)

サンプル

var input = ["a", "b", "c", "b", "e"];
ko.utils.arrayRemoveItem(input, "b");
console.log(input);

結果

["a", "c", "b", "e"]


ここのブログと内容がかぶったけどCoffeeではなくJavaScriptで書いたのでご愛嬌。
http://weathercook.hatenadiary.jp/entry/2013/01/06/211720

ko.computedとko.pureComputed

これはKnockoutJSアドベントカレンダー5日目の記事です。


KnockoutJS Advent Calendar 2014 - Qiita


依存性の追跡(トラッキング)は ko.observable / ko.observableArray と ko.computed の組み合わせで行います。

例えば、生まれた年(西暦)から現在の年齢を算出するのをKnockoutを使って書くと次のようになります。

    <p>Enter your birth year: <input data-bind='value: birthYear' maxlength="4"/></p>
    <p>You're <strong data-bind='text: age'></strong> years old.</p>

    <script>
      function ViewModel() {
        var self = this;
        self.birthYear = ko.observable();
        self.age = ko.computed(function(){
          var now = new Date();
          var age = now.getFullYear() - parseInt(self.birthYear());
          if(isNaN(age)) return 0;
          return age;
        });
      }
      ko.applyBindings(new ViewModel());
    </script>
  • (値が変わらなくても)常に通知するようにしたり、一定時間経ってから通知したりする

上記のサンプルにあるように ko.computed の中で呼ばれる ko.observable / ko.observableArray の値が変わると ko.computed が呼ばれます。

逆にいうと値が変わらないと通知されないのですが、これを常に通知するように簡単に拡張できます。

myViewModel.fullName = ko.pureComputed(function() {
    return myViewModel.firstName() + " " + myViewModel.lastName();
}).extend({ notify: 'always' });


上記のように computed に .extend({ notify: 'always'}) をつけるだけです。

この書き方は extender と呼ばれ、 notify 以外にも rateLimit extender があります。

これは通知されるまでに設定された時間分待ってから通知します。

これは KnockoutJS 3.1.0 で追加されたのでそれ以前のバージョンでは throttle という名前でした(若干動きが違いますが・・・)

また、この extender は簡単に拡張できるのでドキュメントや実際のプロジェクトとか見てみると参考になるかと思います。

ドキュメント
Knockout : Using extenders to augment observables

実際のプロジェクト
knockout.persist/knockout.persist.js at master · spoike/knockout.persist · GitHub



もし computed 内で呼び出している observable A と observable B のうち、

observable A が変更したときだけ computed を実行して、observable B が変更されても computed が実行されない場合はどうしたらよいでしょうか。

これをするには computed 内の observable B に peek() を最後に追加するだけで実現できます。



  • ko.pureComputed とは何か

ko.pureComputed は Knockout 3.2.0 で追加されました。

公式サイトによるとこの機能を使うことにより以下の点で改善があるようです。

1. メモリーリークを防ぐ

2. 計算のオーバーヘッドを防ぐ

これはHTMLに現在バインディングされていない(アクティブでない) pureComputed は実行されないということです。

公式ドキュメントのサンプルがよくできているのでこれを使って説明します。

http://knockoutjs.com/documentation/computed-pure.html

<div class="log" data-bind="text: computedLog"></div>
<!--ko if: step() == 0-->
    <p>First name: <input data-bind="textInput: firstName" /></p>
<!--/ko-->
<!--ko if: step() == 1-->
    <p>Last name: <input data-bind="textInput: lastName" /></p>
<!--/ko-->
<!--ko if: step() == 2-->
    <div>Prefix: <select data-bind="value: prefix, options: ['Mr.', 'Ms.','Mrs.','Dr.']"></select></div>
    <h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
<!--/ko-->
<p><button type="button" data-bind="click: next">Next</button></p>
function AppData() {
    this.firstName = ko.observable('John');
    this.lastName = ko.observable('Burns');
    this.prefix = ko.observable('Dr.');
    this.computedLog = ko.observable('Log: ');
    this.fullName = ko.pureComputed(function () {
        var value = this.prefix() + " " + this.firstName() + " " + this.lastName();
        // Normally, you should avoid writing to observables within a pure computed
        // observable (avoiding side effects). But this example is meant to demonstrate
        // its internal workings, and writing a log is a good way to do so.
        this.computedLog(this.computedLog.peek() + value + '; ');
        return value;
    }, this);
 
    this.step = ko.observable(0);
    this.next = function () {
        this.step(this.step() === 2 ? 0 : this.step()+1);
    };
};
ko.applyBindings(new AppData());


このサンプルでは表示するHTMLをNextボタンを押す度に次のステップになり、ステップ0〜2 の 3回に分けて表示しています。

ステップ0では First name の入力、ステップ1では Last name の入力、ステップ2では Prefix の入力と fullName の表示を行っています。

ここで注目すべきところは、fullName が ko.pureComputed で定義されていることです。

fullNameの中では First name, Last name, Prefix を呼び出しているにも関わらず、fullName が実行されるのは

fullNameが実際にバインドされる「ステップ2」の段階になったときです。

今まで使っていた ko.computed だとどのステップにも関わらず実行されていたものを、ちゃんと「今つかっているか」を判断し、計算してくれます。

ko.pureComputed はComponentsを使った大規模なSPAでは、特に必要になってくるため地味な機能に見えて大変重要な機能ではないのかと思っています。

実際にサンプルも今まで ko.computed で書いてあったところを ko.pureComputed で書き直していたりしているので、

これからは ko.computed よりも ko.pureComputed を使った方が良い気がします。


逆に ko.computed を使う場面としては公式ドキュメントには以下のように書いてあります。

Knockout : Pure computed observables


1. 複数の observable に影響があるコールバックを ko.computed で使う場合

ko.computed(function () {
    var cleanData = ko.toJS(this);
    myDataClient.update(cleanData);
}, this);

2. バインディングの init メソッドで要素を更新するために ko.computed を使う場合

ko.computed({
    read: function () {
        element.title = ko.unwrap(valueAccessor());
    },
    disposeWhenNodeIsRemoved: element
});


用途として基本的には pureComputedを使い、初期値やサーバサイドで値が変わったら常に実行したい場合は ko.computed を使う感じなのかなと思います。



明日は @yusuke_nozoe さんです!よろしくお願いします!

Components を使ってみる

これはKnockoutJSアドベントカレンダー3日目の記事です。


KnockoutJS Advent Calendar 2014 - Qiita


KnockoutJS 3.2では新しく Components という機能が追加されました。

作者のブログにもあるようにこの機能は web components に強く影響された機能で、コンポーネント単位で非同期にページをレンダリングします。

Componentsを使うとどのように書くことができるのか見てみましょう。
まず普通のKnockoutで書いたサンプルです。

<html lang="ja">
<html>
  <head>
    <title> Component Examples</title>
    <script src="knockout-3.2.0.js"></script>
  </head>
  <body>
    
    <p>Enter your name: <input data-bind='value: name'/></p>
    <p>You entered <strong data-bind='text: name().toUpperCase()'></strong></p>
    
    <script>
      var viewModel = {
        name: ko.observable('something')
      };
      ko.applyBindings(viewModel);
    </script>
  </body>
</html>

デモはこちら

このデモを全く同じ動作のまま Components を使って書くと以下のようになります。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <title> Component Example </title>
    <script src="js/knockout-3.2.0.js"></script>
  </head>
  <body>
  	<name-editor></name-editor>
    <script>
    	ko.components.register('name-editor', {
    		template: "<p>Enter your name: <input data-bind='value: name'/></p><p>You entered <strong data-bind='text: name().toUpperCase()'></strong></p>",
    		viewModel: function() {
    			this.name = ko.observable('something');
    		}
    	});
    	ko.applyBindings();
    </script>
  </body>
</html>

このデモはこちら

まったく同じ動作ですが、ko.components.register を使い "name-editor" という名前のComponentを登録しています。
これによりコンポーネントの再利用することが可能になります。


またコンポーネントは作成するときに以下のようにパラメータを渡してやることができます。
KnockoutJSのComponentsでは "component"バインディングも用意されており、他のバインディングと同じように data-bind でコンポーネントを指定することもできます。

デモはこちら

<!DOCTYPE html>
<html lang="ja">
  <head>
    <title> Component Example </title>
    <script src="js/knockout-3.2.0.js"></script>
  </head>
  <body>
    <name-editor params="value: 'hello world'"></name-editor>
    <div data-bind="component: { name: 'name-editor', params: { value: 'hoge bar' } }"></div>

    <script>
    	ko.components.register('name-editor', {
    		template: "<p>Enter your name: <input data-bind='value: name'/></p><p>You entered <strong data-bind='text: name().toUpperCase()'></strong></p>",
    		viewModel: function(params) {
    			this.name = ko.observable(params.value);
    		}
    	});
    	ko.applyBindings();
    </script>
  </body>
</html>

いかがでしたでしょうか。
ざっと軽く見ただけでも便利なのが分かってもらえるかと思います。

さらに、KnockoutJSはこの機能もIE6のような古いブラウザーでも動くように実装しているらしいですが未検証です。

明日は @sukobuto さんの 「THETA BINDING」 です!よろしくお願いします!

JVM仕様にあるクラスファイルのフォーマットについて

これはJVMアドベントカレンダー1日目の記事です。


JVM Advent Calendar 2014 - Qiita


JVM仕様にあるクラスファイルのフォーマットについて書きます。

JavaのクラスファイルのフォーマットはJVM仕様の4章で定義されています。

Chapter 4. The class File Format

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}


全く分からない状態からこれらのフォーマットについてひとつづつ読んでいくのは辛いと思うのでざっくり説明します。

クラスファイルが必要なので適当なJavaソースコードを作ってコンパイルします。

class AdventCalendar2014 {
    public static void main(String[] args) {
        System.out.println("Hello JVM Advent Calendar 2014!");
    }
}
% javac AdventCalendar2014.java
% ls
AdventCalendar2014.class AdventCalendar2014.java


クラスファイルが出来たので今度はバイナリを覗いてみましょう。
以前そういうやつ書いたことがあるのでこれを使うことにします。


ClassReader

これをそのままコピってきてコンパイルしてさっきのクラスファイルを引数に指定すると、
2進数と16進数でクラスファイルの中身を出力します。

% java ClassReader /path/to/AdventCalendar2014.class
Binary  |Hex
------------
11001010 CA
11111110 FE
10111010 BA
10111110 BE
00000000 0
00000000 0
00000000 0
00110100 34
00000000 0
00011101 1D
00001010 A
00000000 0
00000110 6
00000000 0
00001111 F
00001001 9
00000000 0
00010000 10
00000000 0
00010001 11
00001000 8
00000000 0
00010010 12
00001010 A
00000000 0
00010011 13
00000000 0
00010100 14
00000111 7
00000000 0
00010101 15
00000111 7
00000000 0
00010110 16
00000001 1
00000000 0
00000110 6
00111100 3C
01101001 69
01101110 6E
01101001 69
01110100 74
00111110 3E
・・・

2進数は読みにくいので16進数だけにしたら以下のような感じになりました。

CAFEBABE00000034001D0A0006000F09001000110800120A001300140700150700160100063C696E
69743E010003282956010004436F646501000F4C696E654E756D6265725461626C650100046D6169
6E010016285B4C6A6176612F6C616E672F537472696E673B295601000A536F7572636546696C6501
0017416476656E7443616C656E646172323031342E6A6176610C000700080700170C001800190100
1F48656C6C6F204A564D20416476656E742043616C656E64617220323031342107001A0C001B001C
010012416476656E7443616C656E646172323031340100106A6176612F6C616E672F4F626A656374
0100106A6176612F6C616E672F53797374656D0100036F75740100154C6A6176612F696F2F507269
6E7453747265616D3B0100136A6176612F696F2F5072696E7453747265616D0100077072696E746C
6E010015284C6A6176612F6C616E672F537472696E673B2956002000050006000000000002000000
070008000100090000001D00010001000000052AB70001B100000001000A00000006000100000002
0009000B000C00010009000000250002000100000009B200021203B60004B100000001000A000000
0A000200000005000800060001000D00000002000E


これを最初のJVM仕様のクラスフォーマットと照らし合わせていきましょう。
magic っていうのはマジックナンバーJavaクラスの先頭はこの値が入ります。
これは Cafe(C++, A Front End)というC++コンパイラのプロダクトマネージャの Kim Polese さんの愛称(babe)からきているというのがまことしやかに噂として流れています。

CA FE BA BE // u4 magic;
00 00       // u2 minor_version;
00 34       // u2 major_version;
00 1D       // u2 constant_pool_count;

ってみていくと cp_info っていうよく分からないのが登場しました。
これもJVM仕様読んでいくと 4.4 で以下のように定義されています。

cp_info {
    u1 tag;
    u1 info[];
}

一番最初の tag でデータの種類を定義していて、その値によって次の info[] の読み方が変わります。
例えば tag = 7 の場合は CONSTANT_Class なので、その場合は CONSTANT_Class_info を読みます。
というような感じで進めていくと以下のようになります。

CA FE BA BE     // u4 magic
00 00           // u2 minor_version = 0
00 34           // u2 major_version = 57
00 1D           // u2 constant_pool_count = 29
// cp_info #1
0A              // tag = 10 (CONSTANT_Methodref)
00 06           // u2 class_index = 6
00 0F           // u2 name_and_type_index = 15
// cp_info #2
09              // tag = 9 (CONSTANT_Fieldref)
00 10           // u2 class_index = #16
00 11           // u2 name_and_type_index = #17
// cp_info #3
08              // tag = 8 (CONSTANT_String)
00 12           // u2 string_index = #18
// cp_info #4
0A              // tag = 10 (CONSTANT_Methodref)
00 13           // u2 class_index = #19
00 14           // u2 name_and_type_index = #20
// cp_info #5
07              // tag = 7 (CONSTANT_Class)
00 15           // u2 name_index = #21
// cp_info #6
07              // tag = 7 (CONSTANT_Class)
00 16           // u2 name_index = #18
// cp_info #7
01              // u1 tag = 1 (CONSTANT_Utf8)
00 06           // u2 length = 6
3C 69 6E 69 74  // u1 bytes[length] = <init>
3E
// cp_info #8
01              // u1 tag = 1 (CONSTANT_Utf8)
00 03           // u2 length = 3
28 29 56        // u1 bytes[length] = ()V
// cp_info #9
01              // u1 tag = 1 (CONSTANT_Utf8)
00 04           // u2 length = 4
43 6F 64 65     // u1 bytes[length] = Code
// cp_info #10
01              // u1 tag = 1 (CONSTANT_Utf8)
00 0F           // u2 length = 15
4C 69 6E 65 4E  // u1 bytes[length] = LineNumberTable
75 6D 62 65 72 
54 61 62 6C 65 
// cp_info #11
01              // u1 tag = 1 (CONSTANT_Utf8)
00 04           // u2 length = 4
6D 61 69 6E     // u1 bytes[length] = main
// cp_info #12
01              // u1 tag = 1 (CONSTANT_Utf8)
00 16           // u2 length = 22
28 5B 4C 6A 61  // u1 bytes[length] = ([Ljava/lang/String;)V
76 61 2F 6C 61
6E 67 2F 53 74
72 69 6E 67 3B
29 56 
// cp_info #13
01              // u1 tag = 1 (CONSTANT_Utf8)
00 0A           // u2 length = 10
53 6F 75 72 63  // u1 bytes[length] = SourceFile
65 46 69 6C 65 
// cp_info #14
01              // u1 tag = 1 (CONSTANT_Utf8)
00 17           // u2 length = 23
41 64 76 65 6E  // u1 bytes[length] = AdventCalendar2014.java
74 43 61 6C 65 
6E 64 61 72 32 
30 31 34 2E 6A 
61 76 61 
// cp_info #15
0C              // u1 tag = 12 (CONSTANT_NameAndType)
00 07           // u2 name_index = #7
00 08           // u2 descriptor_index = #8
// cp_info #16
07              // tag = 7 (CONSTANT_Class)
00 17           // u2 name_index = #23
0C              // u1 tag = 12 (CONSTANT_NameAndType)
00 18           // u2 name_index = #24
00 19           // u2 descriptor_index = #25
・・・


まぁあとは同じです。。
なんと驚くべきことに、ここまですべて手作業で解析してきましたが、さすがにもうしんどいです。

Javaチョットデキル人はご存知の人も多いですが、この解析は javap すれば簡単に出力してくれます。

% javap -c -verbose AdventCalendar2014
Classfile /path/to/AdventCalendar2014.class
  Last modified 2014/11/30; size 461 bytes
  MD5 checksum 94ccbcbc2ad1fdedf9c05cf088ca89f5
  Compiled from "AdventCalendar2014.java"
class AdventCalendar2014
  minor version: 0
  major version: 52
  flags: ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            // Hello JVM Advent Calendar 2014!
   #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #21            // AdventCalendar2014
   #6 = Class              #22            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               AdventCalendar2014.java
  #15 = NameAndType        #7:#8          // "<init>":()V
  #16 = Class              #23            // java/lang/System
  #17 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
  #18 = Utf8               Hello JVM Advent Calendar 2014!
  #19 = Class              #26            // java/io/PrintStream
  #20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V
  #21 = Utf8               AdventCalendar2014
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/String;)V
{
  AdventCalendar2014();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello JVM Advent Calendar 2014!
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 5: 0
        line 6: 8
}
SourceFile: "AdventCalendar2014.java"


こんな感じです。

明日は @sugarlife さんの「OpenJDKのソースコード配置(1-2サブディレクトリ程度まで)」です!
よろしくお願いします!

KnockoutJSの紹介

これはKnockoutJSアドベントカレンダー1日目の記事です。


KnockoutJS Advent Calendar 2014 - Qiita


一発目なので最初は KnockoutJS とはどんなものなのかについて書きたいと思います。

KnockoutJSとはMVVMパターンJavaScriptで作る動的なUIをシンプルに構築するライブラリです。
※個人的にはフレームワークではなくライブラリだと思っている


公式サイトには以下のような標語で書かれています。

Simplify dynamic JavaScript UIs with the Model-View-View Model (MVVM)

Knockout : Home


KnokoutJSを一言でいえば、「バインディングフレームワーク」の一種です。

以下の特徴があります。

・他のライブラリ(jQueryなど)に依存しない
IE 6+, Firefox 3.5+, Chrome, Opera, Safari など比較的古いブラウザに対応している


個人的にKnockoutJSが好きな箇所としては主に以下の2点です。

(1) 余計な機能があまりない(学習コストが高くない)
(2)(前述の特徴にもあるように)古いブラウザに対応している


まず、(1)ですが、KnockoutJSでは「data-bind=""」って空でタイプすることができれば7割ぐらいKnockoutをマスターしたものだと思ってもらっても過言ではないです。
3.1までは・・・。

それほどまでに余分な機能は削ぎ落されていてはいるものの、拡張性も高く様々なプラグインも用意されています。


Plugins · knockout/knockout Wiki · GitHub


プラグインは上記ページだけではなく、他にも


SteveSanderson/knockout.mapping · GitHub



SteveSanderson/knockout-projections · GitHub


SteveSanderson/knockout-es5 · GitHub

などといったKnockoutJS作者本人が作ったプラグインも数多くあるので見てみるのもいいと思います。
knockout-mappingについてはまた後日説明しようと思います。

(2) については KnockoutJSはVue.jsとは違い、レガシー・ブラウザもサポートしています。
その代わりVue.jsほどスタイリッシュではないし色々内部で泥臭いこともやっているのですが、現実解として古いブラウザーもサポートしないといけない場面は実際の仕事ではよくあることなのではないでしょうか?


というわけで明日は @hakurai さんです!たぶん Haxe について書いてくれると思います!

OpenJDKをビルドしてみた

デバッグ版のJVMが欲しかったのでビルドしてみた。

環境はMac

基本ソースをMercurialでクローンしてREADME読めばできる。

READMEにもあるけど、詳しいビルド方法はここを参照すればいい。
http://hg.openjdk.java.net/jdk8/jdk8/raw-file/tip/README-builds.html

以下、作業手順。

デバッグ版が欲しい場合は Configure時に--enable-debug するとよい。

% hg clone http://hg.openjdk.java.net/jdk8/jdk8 jdk8_src
requesting all changes
adding changesets
adding manifests
adding file changes
added 942 changesets with 1309 changes to 136 files
updating to branch default
82 files updated, 0 files merged, 0 files removed, 0 files unresolved

% cd jdk8_src
% ./get_source.sh
# Repositories:  corba jaxp jaxws langtools jdk hotspot nashorn

                corba:   hg clone http://hg.openjdk.java.net/jdk8/jdk8/corba corba
                 jaxp:   hg clone http://hg.openjdk.java.net/jdk8/jdk8/jaxp jaxp
                 jaxp:   requesting all changes
                corba:   requesting all changes
                corba:   adding changesets
                 jaxp:   adding changesets
Waiting 5 secs before spawning next background command.
                 jaxp:   adding manifests
                corba:   adding manifests
                jaxws:   hg clone http://hg.openjdk.java.net/jdk8/jdk8/jaxws jaxws
            langtools:   hg clone http://hg.openjdk.java.net/jdk8/jdk8/langtools langtools
                jaxws:   requesting all changes
            langtools:   requesting all changes
                jaxws:   adding changesets
            langtools:   adding changesets

〜 中略 〜
                    .:   cd . && hg pull -u
              ./corba:   cd ./corba && hg pull -u
            ./hotspot:   cd ./hotspot && hg pull -u
               ./jaxp:   cd ./jaxp && hg pull -u
              ./jaxws:   cd ./jaxws && hg pull -u
                ./jdk:   cd ./jdk && hg pull -u
          ./langtools:   cd ./langtools && hg pull -u
            ./nashorn:   cd ./nashorn && hg pull -u
            ./hotspot:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/hotspot
            ./nashorn:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/nashorn
              ./jaxws:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/jaxws
               ./jaxp:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/jaxp
          ./langtools:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/langtools
                    .:   pulling from http://hg.openjdk.java.net/jdk8/jdk8
              ./corba:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/corba
                ./jdk:   pulling from http://hg.openjdk.java.net/jdk8/jdk8/jdk
            ./hotspot:   searching for changes
            ./hotspot:   no changes found
              ./jaxws:   searching for changes
              ./jaxws:   no changes found
               ./jaxp:   searching for changes
               ./jaxp:   no changes found
            ./nashorn:   searching for changes
            ./nashorn:   no changes found
          ./langtools:   searching for changes
          ./langtools:   no changes found
              ./corba:   searching for changes
              ./corba:   no changes found
                    .:   searching for changes
                    .:   no changes found
                ./jdk:   searching for changes
                ./jdk:   no changes found

% bash ./configure --enable-debug --with-target-bits=64
Running generated-configure.sh
configure: Configuration created at Sat Oct  4 01:07:52 JST 2014.
configure: configure script generated at timestamp 1389186094.
checking for basename... /usr/bin/basename
checking for bash... /bin/bash
checking for cat... /bin/cat
checking for chmod... /bin/chmod
checking for cmp... /usr/bin/cmp

〜 中略 〜

checking if build directory is on local disk... yes
configure: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/config.status
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/spec.gmk
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/hotspot-spec.gmk
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/bootcycle-spec.gmk
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/compare.sh
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/spec.sh
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/Makefile
config.status: creating /Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug/config.h

====================================================
A new configuration has been successfully created in
/Users/plugram/Desktop/jvm/jdk8_src/build/macosx-x86_64-normal-server-fastdebug
using configure arguments '--enable-debug --with-target-bits=64'.

Configuration summary:
* Debug level:    fastdebug
* JDK variant:    normal
* JVM variants:   server
* OpenJDK target: OS: macosx, CPU architecture: x86, address length: 64

Tools summary:
* Boot JDK:       java version "1.8.0_20" Java(TM) SE Runtime Environment (build 1.8.0_20-b26) Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)  (at /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home)
* C Compiler:     i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build) version 2335.15.00) (at /usr/llvm-gcc-4.2/bin/llvm-gcc-4.2)
* C++ Compiler:   i686-apple-darwin11-llvm-g++-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build) version 2335.15.00) (at /usr/llvm-gcc-4.2/bin/llvm-g++-4.2)

Build performance summary:
* Cores to use:   2
* Memory limit:   4096 MB
* ccache status:  not installed (consider installing)

Build performance tip: ccache gives a tremendous speedup for C++ recompilations.
You do not have ccache installed. Try installing it.


% make all
Building OpenJDK for target 'all' in configuration 'macosx-x86_64-normal-server-fastdebug'

## Starting langtools
Compiling 2 files for BUILD_TOOLS
Compiling 31 properties into resource bundles
Compiling 777 files for BUILD_BOOTSTRAP_LANGTOOLS
Creating langtools/dist/bootstrap/lib/javac.jar
Updating langtools/dist/lib/src.zip
Compiling 780 files for BUILD_FULL_JAVAC
Creating langtools/dist/lib/classes.jar
## Finished langtools (build time 00:01:02)

## Starting hotspot
make[2]: warning: -jN forced in submake: disabling jobserver mode.
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
Creating Makefile ...
Creating directory list ../shared_dirs.lst
Creating flags.make ...
Creating flags_vm.make ...
Creating vm.make ...
Creating adlc.make ...
Creating jvmti.make ...
Creating trace.make ...
Creating sa.make ...
Creating dtrace.make ...
INFO: ENABLE_FULL_DEBUG_SYMBOLS=1
Creating Makefile ...
Creating flags.make ...
Creating flags_vm.make ...
Creating vm.make ...
Creating adlc.make ...
Creating jvmti.make ...
Creating trace.make ...

〜 中略 〜

## Finished docs (build time 00:03:48)

----- Build times -------
Start 2014-10-04 01:10:54
End   2014-10-04 01:39:25
00:00:50 corba
00:01:00 demos
00:03:48 docs
00:12:07 hotspot
00:01:27 images
00:00:30 jaxp
00:00:44 jaxws
00:06:44 jdk
00:01:02 langtools
00:00:18 nashorn
00:28:31 TOTAL
-------------------------
Finished building OpenJDK for target 'all'

% make install
Building OpenJDK for target 'install' in configuration 'macosx-x86_64-normal-server-fastdebug'

## Starting langtools
## Finished langtools (build time 00:00:01)

## Starting hotspot
## Finished hotspot (build time 00:00:00)

## Starting corba
## Finished corba (build time 00:00:00)

## Starting jaxp
## Finished jaxp (build time 00:00:01)

## Starting jaxws
## Finished jaxws (build time 00:00:00)

## Starting jdk
## Finished jdk (build time 00:00:03)

## Starting demos
## Finished demos (build time 00:00:00)

## Starting nashorn
## Finished nashorn (build time 00:00:00)

## Starting images
Copying images/j2re-bundle/jre1.8.0.jre/Contents/Home/release
Copying images/j2sdk-bundle/jdk1.8.0.jdk/Contents/Home/.DS_Store
Copying images/j2sdk-bundle/jdk1.8.0.jdk/Contents/Home/jre
Copying images/j2sdk-bundle/jdk1.8.0.jdk/Contents/Home/jre/.DS_Store
Copying images/j2sdk-bundle/jdk1.8.0.jdk/Contents/Home/release
## Finished images (build time 00:00:04)

## Starting install
Installing jdk image into /usr/local/jvm/openjdk-1.8.0-internal-fastdebug
and creating 40 links from /usr/local/bin into the jdk.
## Finished install (build time 00:00:04)

----- Build times -------
Start 2014-10-04 01:54:51
End   2014-10-04 01:55:04
00:00:00 corba
00:00:00 demos
00:00:00 hotspot
00:00:04 images
00:00:04 install
00:00:01 jaxp
00:00:00 jaxws
00:00:03 jdk
00:00:01 langtools
00:00:00 nashorn
00:00:13 TOTAL
-------------------------


インストールしたら /usr/local/bin に入った。

さっそく実験。

以下のようなソースを書いて実行。

class Hello {
  public static void main(String[] args) {
    new Hello().hello();
  }

  public int hello() {
    int result = 1 + 2;
    return result;
  }
}
% /usr/local/bin/javac Hello.java
% /usr/local/bin/java -client -Xcomp -XX:+PrintIRWithLIR -XX:CompileOnly="Hello.hello" Hello
__bci__use__tid____instr____________________________________
. 0    0     4     B1 [0, 0] -> B2 sux: B2

     label [label:0xec065838]
. 0    0     7     std entry B2
     std_entry
     move [rsi|L] [R177|L]
     move [metadata:0xa1684420|M] [R178|M]
     move [Base:[R178|M] Disp: 108|I] [R179|I]
     add [R179|I] [int:8|I] [R179|I]
     move [R179|I] [Base:[R178|M] Disp: 108|I]
     move [metadata:0xa16842f0|M] [R180|M]
     logic_and [R179|I] [int:0|I] [R179|I]
     cmp [R179|I] [int:0|I]
     branch [EQ] [CounterOverflowStub: 0xec067718]
     label [label:0xec067740]
     branch [AL] [B2]

__bci__use__tid____instr____________________________________
. 0    0     5     B2 (S) [0, 0] -> B0 dom B1 sux: B0 pred: B1

     label [label:0xec065b48]
. 0    0     6     goto B0
     branch [AL] [B0]

__bci__use__tid____instr____________________________________
. 0    0     0     B0 (SV) [0, 3] dom B2 pred: B2

     label [label:0xec064e38]
. 3    0    i3     ireturn i2
     move [int:3|I] [rax|I]
     return [rax|I]

__bci__use__tid____instr____________________________________
. 0    0     4     B1 [0, 0] -> B0 sux: B0

     label [label:0xec8a8a38]
. 0    0     5     std entry B0
     std_entry
     move [rsi|L] [R177|L]
     branch [AL] [B0]

__bci__use__tid____instr____________________________________
. 0    0     0     B0 (SV) [0, 3] dom B1 pred: B1

     label [label:0xec8a8038]
. 3    0    i3     ireturn i2
     move [int:3|I] [rax|I]
     return [rax|I]

とりあえず今日はここまで。

友達から「これ無料で作ってよ」って言われたときに注意したいこと

最近あまりにもひどいので、今後同じような経験をしないよう備忘録として書いておきます。

プログラマとかデザイナーの人は一度は必ず経験があると思うのですが、
友人から「こんなアイデアがあるんだけど」「うちの会社のサイトがダサくてね」という切り出しで、
「"ちょっと"やってくれない?」って頼まれることがあります。


この"ちょっと"が曲者で、

・"ちょっと" だからお金を払わなくていいよね?
・"ちょっと" だからまた頼むね


って感じで友人という立場上、依頼者は頼みやすいので
最初は自分も特に嫌ではなく、やってしまうことがあります。


ですが、この次の "ちょっと" を "何となく" やってしまったため、
次もまた次も頼まれることになります。

こうなってくると、最初は無料で感謝してた友人の依頼者も
なぜか「当たり前にやってくれる」ものとして依頼してきて、
こちらがやりたいと思わないことも要求してきます。


こういうパターンを何回か経験しているので、いい加減自分も学習しろと思うのですが、
こうなってくるともう相手は自分のやりたいと思っていることも聞いてくれず、
また自分的にも単なる作業にしかならない仕事を休日を使ってやることになります。


土日作業し休みを費やしてやっても感謝されず(実際はありがとうの一言ぐらいは言ってもらえる)
一文も儲からないし技術的にもやりたいこととは違うので、やりたくないと思い始めるのですが、
問題はここで「やめる」といっても、依頼者は自分にはできないので「困る」としか言えません。


なので、とりあえず引き継ぐ方向性を探るのですが、そもそも無料でこんなことやりたい人なんかいないので、もちろん誰も見つかりません。

こうなってくるとその友人に会うのすら苦痛になり、友人関係も崩壊します。



そんな経緯が度々あったので、今後誰に頼まれても「無料で」なにかのアプリとかサイトを作るつもりは一切ありません。

勉強会で発表するのは続けたいですが、あくまで「自分がやりたいこと」と「終了期限や終了条件」が明確であることが前提です。


たぶん同じような状況になったことのある人も結構いると思うし、
現在こんな感じの状況の人も結構いると思う。


なのでそういう状況でいま困っている人は最初に「無料」でやる仕事の「終了条件」を
必ず依頼者と合意しておくことをお勧めします。


あと、アプリやサイトを無料で依頼してる人は「タダより高いものはない」ということ、
「アプリを作る」「サイトを運営する」には一定以上の覚悟と投資がないとできないと思っておいたほうがよいと思います。