ソラマメブログ

2007年11月30日

エラーの種類と対処方法

エラーの種類と原因、対処方法についてまとめてみました。
LSLに関わって発生するエラーには主に3種類あります。

+コンパイルエラー(Compiler Errors)
+ランタイムエラー(Run-Time Errors)
+ビルドエラー(Build Errors)

以下詳細にエラーメッセージごとに原因や対処方法を書いています。
なにやらエラーメッセージに遭遇したとき、あわてず騒がず、原因を見極めて対応するために参考になればと思います。
コンパイルエラー

スクリプトコードを保存したときに発生するエラーです。
コードの記述間違いや抜けによって起きます。
エラー個所を修正して保存し直さないとスクリプトを動作させることはできません。

修正する際にはエラーメッセージとともに表示される行・列番号が参考になります。
 (3,8):ERROR:Syntax Error

このような表示は、3行目の8文字目辺りに間違いがあることを意味します。
ただしあくまでも参考情報ですので、その位置ではない個所に間違いが潜んでいる場合もあります(特にSyntaxErrorの場合)。
なお、ここに表示される行数・文字数は0行目0文字目から数えることに注意して下さい(先頭行は1行目ではなく0行目です)。

コンパイルエラーには以下のような種類があります。

Syntax Error

シンタックスエラー、文法エラーとも言います。
普段遭遇するエラーの8割はこれです。
エラーの代名詞みたいな存在です。

コンパイルという作業は、コンピュータがコードの意味を解釈してマシンコードに変換していく作業ですが、このエラーはコンピュータがコードの意味を理解できなかったことを意味します。
例えば日本語で言うなら、以下のようなものはシンタックスエラーです。
「昨日怪獣を悲しくてまたの名をニュージーランド旅行が久しからず」
意味不明で解釈できないので、これは日本語としてエラーになります。

LSLにも文法がありますので、文法を間違うとシンタックスエラーが出ます。
例えばありがちなのは、「文末には;を付ける」という文法をうっかり忘れた場合です。

 default {
   state_entry(){
     llSay(0,"Yes.") ←文末に;が無い
   }
 }


他には、{}や()の対応付けを間違うというパターンもあります。
if文などによる条件分岐が深くなると、うっかり閉じカッコを忘れたりします。

 default {
   touch_start(integer detected){
     if (llDetectedKey(0) == llOwner()){
       if (llVecDist(llDetectedPos(0), llGetPos()) < 4.0){
         if (llDetectedName(0) == "Miz Cremorne"){
           llSay(0,"Hi, Miz!");
         }
       }
       ← ここでもう一つ}が必要
   }
 }


このパターンのシンタックスエラーはエラー個所を示す行番号がズレるので気付きにくいエラーです。

Type mismatch

タイプミスマッチ、型不正とも言います。
変数には[[型>基礎知識/変数とは?]]がありますが、異なる型同士で演算を行おうとした場合などに起こります。

日本語を例にとると、例えば、
「彼は小さいときには皮を被っていたが、大きくなったら皮がむけてとっても堅くなった」
と言うと、なんだか誤解を与えます。
これは実は「タケノコ」についての記述であり、「彼」という型を使うのは間違いなのです。
「彼」のかわりに「それ」を使ってもあんまり変わらないという突っ込みはご容赦下さい。

LSLでタイプミスマッチが起こるのは以下のような場合です。

 default {
   state_entry(){
     integer i;
     i = "Takenoko";
   }
 }


変数iはinteger型(整数型)なのに、文字列の"Takenoko"を代入しようとしているのでエラーとなります。

Function call mismatches type or number of arguments

引数不正によるエラーです。
直訳すると「不正な型、または不正な数の引数を使って関数が呼ばれています」という感じでしょうか。

下手な喩えをまた使いますが、電子レンジというものは中に入れるのは通常「食べ物」と決まっています(例外もありますがw)。
にも関わらず、雨に濡れた猫を乾かそうとして電子レンジに入れたりすると、妙な都市伝説が生まれる結果になります。

同じように、関数はそれぞれ、指定すべき引数の型と数、順序が決まっています。
決まっている引数以外のものを指定したり、間違った順序で書いたりすると、このエラーが発生します。
どのような引数を使うべきかは関数によって様々ですので、[[リファレンス>リファレンス(名前順)]]を参照して調べて下さい。

例えばllSayという関数は、
 llSay(integer channel, string msg)

整数と文字列の二つの引数を指定すると決まっています。
これを間違えて以下のように書くとエラーです。

 default {
   state_entry(){
     llSay("cat", 0);
   }
 }


Name not defined within scope

未定義エラーです。
定義されていない変数やユーザー関数を使ったときに発生します。
要するに「そんな言葉は知らないよ!」というエラーです。

日本語で言うなら、
「昨日さぁ、パップラドンカルメ食べたんだ~」
という感じですかね。
相手が人間なら「パップラドンカルメって何?」と聞けますが、コンピュータはシャイなので「それ何?」の一言が言えません。
変数やユーザー関数を使うときには、必ず前もって「定義」して、コンピュータがわかるようにしてあげなければいけません。

自分では定義したつもりでも、うっかりミスで未定義の変数を書いてしまうこともあります。
エラーの原因としてはこちらのほうが多いのではないでしょうか。

 default {
   state_entry(){
     string pappuradon = "karume";
     llSay(0, papuradon); ← うっかりpが抜けた
   }
 }


落ち着いてよく見ればすぐわかるミスだと思いますので、このエラーが出たときはよくコードを見直してみましょう。

また、変数には「スコープ」という概念があります。
ごく簡単に言ってしまうと、ある変数が有効なのはその変数が定義された{}の中だけです。
定義された{}の外でその変数を使おうとすると、やはりこのエラーが出ます。

 default {
   state_entry(){
     string pappuradon = "karume";
   } ← 変数pappuradonが有効なのはここまで
 
   touch_start(integer detected){
     llSay(0, pappuradon); ←ここは変数のスコープ外
   }
 }


Name previously declared within scope

二重定義エラーです。
同じ名前の変数を同じスコープ内で定義しようとした場合などに起こります。

クラスに「山田太郎君」が2人いるような状況です。
コンピュータは人の顔を覚えるのが苦手ですので、同姓同名だと区別が出来ません。

 default {
   state_entry(){
     string yamada = "taro"; ← 一人目の山田君
     llSay(0, yamada);
     string yamada = "taro"; ← 二人目の山田君。この時点でエラー
     llSay(0, yamada);
   }
 }


仕方がないので二人目の山田君には今日から山下君に改名してもらえば解決です。

 default {
   state_entry(){
     string yamada = "taro"; ← 一人目の山田君
     llSay(0, yamada);
     string yamashita = "taro"; ← 違う名前になったのでOK
     llSay(0, yamashita);
   }
 }


Use of vector or quaternion method on incorrect type

あまり遭遇することのないエラーですが一応紹介しておきます。
ベクター型とローテーション型の変数は、内部の要素にアクセスするためのメソッド「.」を使うことができます。

 vector p = <1.0, 2.0, 3.0>;
 llSay(0, (string)p.x); ← p.xはpの最初の要素。1.0が入っている


この特殊なメソッド「.」をベクター/ローテーション型以外で使うとこのエラーが出ます。

 default {
   state_entry(){
     integer p;
     llSay(0, (string)p.x); ← pはintegerなのでエラー
   }
 }


Lists can't be included in lists

リストにリストを入れようとしたときに起こるエラーです。
リスト型変数には、整数、小数、文字列などなど、あらゆる型のデータを入れることができますが、リスト型データを入れることはできません。

 default {
   state_entry(){
     list a = [1,2,3];
     list b = [4,5,6,a]; ← aはリスト型なので入れられない
   }
 }


二つのリストを連結したいときには、以下のように書き直しましょう。

 default {
   state_entry(){
     list a = [1,2,3];
     list b = [4,5,6] + a; ← aを連結するのでbは[4,5,6,1,2,3]になる
   }
 }


Return statement type doesn't match function return type

ユーザー関数にまつわるエラーです。
戻り値の型が、定義された型と違う場合に発生します。

ユーザー関数と戻り値については[[関数の説明>基礎知識/関数とは?]]の後半を参考にして下さい。

以下のようなコードがエラーの例です。

 integer func(){
   return "hoge";
 }


ユーザー関数funcにはinteger型の戻り値を定義しているのに、実際にreturnで返しているのは文字列です。
戻り値の型定義と実際の型が異なるので、このエラーが発生します。

Function returns a value but return statement doesn't

これもユーザー関数にまつわるエラーです。
ユーザー関数に戻り値が定義されているのに、どこにもreturn文が無い場合に発生します。

 integer func(){
   llSay(0, "It's OK."); ← 全然OKじゃない
 }


integer型を返す、と定義しているのですから、正しくreturn文でinteger型のデータを返すようにしなければいけません。

 integer func(){
   llSay(0, "It's OK.");
   return TRUE;
 }


これならOKです。

Not all code paths return a value

これもユーザー関数にまつわるエラーです。
ユーザー関数内で条件分岐などを多用すると陥りやすいエラーです。

直訳すると「全てのコードが戻り値を返すようになってない」ということになります。

例えば、

 integer func(string mode){
   if (mode == "A"){
     llSay(0, "mode A");
     return 0; ← modeが"A"のときしか戻り値を返さない
   }else if (mode == "B"){
     llSay(0, "mode B");
   }
 }


modeが"B"のときにはretuen文が実行されないので、戻り値が返されません。
ユーザー関数funcはinteger型の戻り値を返すように定義されていますので、必ずreturn文でinteger型のデータを返すようにしなければいけません。

Dead code found beyond return statement

永久に日の目を見ない哀れなコードがあるときに発生します。
以下のような場合です。

 integer func(){
   return 0; ← ここでリターンされる
   llSay(0, "Help!"); ← ここは絶対に実行されない
 }


return以下のコードが不要ならば消去して成仏させましょう。
必要ならばreturnの前に移動し、活躍の舞台を作ってあげて下さい。

Global functions can't change state

ユーザー関数で発生するエラーの一つです。
LSLの仕様で、ユーザー関数内でのステートチェンジは使用不可になっています。

 ChangeState(){
   state active; ← ユーザー関数内でステートチェンジはNG
 }


逃げ道として、以下のように修正する方法があります。

 ChangeState(){
   if (TRUE){
     state active; ← if文に入れると何故かOK
   }
 }


しかしながらこの逃げ道自体がLSLコンパイラーのバグである可能性もあり、オススメは出来ません。

Byte code assembly failed -- out of memory

サイズオーバーのときに出るエラーです。
LSLは1スクリプトで16KByteまでという制限があり、それを越えるような超大作を書いてしまったときにこのエラーになります。

コードを分割し、llMessageLinkedなどを使って通信するように改造して下さい。

ランタイムエラー

コンパイル(コードの保存)は問題なくできたけれども、動かしている最中に発生するエラーのことをランタイムエラーと言います。
ランタイムエラーが起きると、スクリプトの入っているオブジェクトにエラーアイコン(書類のようなマーク)が表示され、デバッグチャンネルにエラーメッセージが表示されます。
クライアント画面の上方、メニューの右あたりにもエラーアイコンが出るので、そこをクリックするとエラーメッセージの内容を確認できます。

エラーメッセージを手がかりにしてコードの修正を行うわけですが、時としてコードのどの位置でエラーになっているのかわからない場合があります。
LSLがどこまで正常に動き、どこでエラーとなるのかを見極めるには、デバッグメッセージを活用するのが常套手段です。

例えば、タッチしたときにランタイムエラーになるとしましょう。
タッチイベントの中には複数の処理が書いてあり、一体どこが問題なのかがさっぱりわからないとします。

 default {
   touch_start(integer detected) {
     float a;
     float b;
     float c;
     for (a = 3.0;a >= 0.0; a -= 1.0){
       b = 10.0 / a;
       c = b * b;
     }
   }
 }


このまま動かしても、タッチイベント内のどこでエラーになるのかわからないので、llOwnerSayを使ってデバッグメッセージを出すように改造します。

 default {
   touch_start(integer detected) {
 llOwnerSay("init start."); ← これがデバッグ用のメッセージ
     float a;
     float b;
     float c;
 llOwnerSay("init end, for-loop start."); ← これがデバッグ用のメッセージ
     for (a = 3.0;a >= 0.0; a -= 1.0){
       b = 10.0 / a;
       c = b * b;
     }
 llOwnerSay("for-loop end."); ← これがデバッグ用のメッセージ
   }
 }


もしも"init start."と表示された後、"init end, for-loop start."の表示が出る前にエラーが起きれば、エラーの原因は変数定義の部分にあるとわかります。
"init end, for-loop start."も表示され、最後の"for-loop end."が出る前にエラーとなれば、for文の中身に問題があるということになります。
このようにして、まずは問題になっている部分を絞り込んでいきます。

仮に"init end, for-loop start."の後にエラーとなったとしましょう。
怪しいのはfor文の中身ということになりますので、さらに詳細にデバッグメッセージを出力するようにします。

 default {
   touch_start(integer detected) {
     float a;
     float b;
     float c;
     for (a = 3.0;a >= 0.0; a -= 1.0){
 llOwnerSay("check: a=" + (string)a
               + ", b=" + (string)b
               + ", c=" + (string)c); ← 変数の中身をデバッグ出力
       b = 10.0 / a;
       c = b * b;
     }
   }
 }


このようにすると、変数a,b,cの中身がどうなっているときにエラーが起きたのかを調べることができます。
出力はたくさん出てきますが、デバッグ時には情報が大事ですので、詳細にスクリプトの動きを調べるべきです。
上記を実行したところ、以下のような結果になったとします。

 Object: check: a=3.0, b=0.0, c=0.0
 Object: check: a=2.0, b=3.3333, c=11.1111
 Object: check: a=1.0, b=5.0, c=25.0
 Object: check: a=0.0, b=10.0, c=100.0
 Object: Script run-time error ← ここでエラーとなった


a=0.0,b=10.0,c=100.0の時にエラーが起きているのがわかります。
では変数の値が上記のような場合の処理をよくよく確かめてみると・・・。

 b = 10.0 / a;
  ↓この計算式は
 b = 10.0 / 0.0
  0で割り算をしているので答えは無限大になってしまう


というのがエラーの原因であると突き止められます。
原因がわかれば、あとは対処するだけです。
aが0.0にならないよう、for文の条件を変えるなどすればOKですね。

以上のように、ランタイムエラーは原因を見つけにくいやっかいなエラーですが、落ち着いてじっくり見極めていけば問題個所は必ず見つかります。
では次に、ランタイムエラーにどのようなものがあるのか見ていきましょう。

Math Error

計算エラーです。
なんらかの計算をした結果、コンピュータでは処理不可能な答えになってしまった場合に発生します。

例えば0による割り算です。
答えは無限大になってしまうのでコンピュータでは扱うことができません。

 default {
   state_entry() {
     float one = 1.0;
     float zero = 0.0;
     float quotient = one / zero; ← ここでエラーとなる
   }
 }


Stack Heap Collision

いわゆるメモリ不足です。
スクリプトが使用可能なメモリサイズは16KByteまでという制限がありますが、そのサイズを越える巨大なデータを扱おうとした場合などにこのエラーになります。

よくありがちなのはリスト型変数にどんどん値を追加していき、膨れ上がったリストがメモリを食いつぶしてエラーとなるケースです。

 default {
   state_entry() {
     list entries = [0];
     while (TRUE) { ← 無限にループ
       entries += entries; ← 要素を延々と追加。やがて制限サイズを越える
       llOwnerSay((string) llGetListLength(entries));
     }
   }
 }


このエラーを予防するにはllGetFreeMemoryを使います。
llGetFreeMemoryはスクリプトが使える残りメモリサイズをByte単位で返してくれます。

例えば、残メモリサイズが1KByte以下になったら処理を中断するのであれば以下のように判定します。

 default {
   state_entry() {
     list entries = [0];
     while (TRUE) { ← 無限にループ
       if (llGetFreeMemory() <= 1024){ ← 残メモリが1KByte以下
         return; ← 処理中断
       }
       entries += entries; ← 要素を延々と追加。
       llOwnerSay((string) llGetListLength(entries));
     }
   }
 }


スクリプトが現在使用しているメモリサイズを得るには以下の式を使います。

 (16 * 1024) - llGetFreeMemory()


不特定数の要素をリストに追加するようなコードを組む際には、必ず予防策を講じておいたほうが無難です。

Too many listens

リッスンが多すぎ!なエラーです。
一つのスクリプトが同時に起動できるllListenは64個が限界です。
それ以上のllListenを使おうとするとこのエラーが出ます。

通常そんなに大量のリッスンを必要とするケースは考えにくいですが、llListenRemoveによるリッスンの閉じ忘れなどによって発生する場合があります。

 default {
   touch_start(integer detected) {
     llListen(channel, "", llDetectedKey(0), ""); ← タッチした人ごとにリッスンを起動
   }
 
   listen(integer channel, string name, key id, string message){
     llSay(0, name + " said, '" + message + "'";
   }
 }
 
 ※リッスンを閉じるコードがどこにもない


Heap Error

「不正なメモリアクセス」が原因で発生するエラーだと思われます。
遭遇したことがないのであまりわかりませんが、ユーザー関数の中からステートチェンジをしたりすると出ることがあるようです。

X error running rule #Y: non-Z rule

引数にリスト型変数を指定するような関数(llSetPrimitiveParams, llParticleSystem等)で起こります。

Xにはエラーとなった関数の名前(llSetPrimitiveParams,llParticleSystem等)が表示されます。
Yは引数のリストのうち、Y番目の要素の型が不正であることを意味します。
Zは本来指定すべき型名です。

 default {
   state_entry(){
     llSetPrimitiveParams([
       PRIM_SIZE, <1, 1, 1, 1>, ← Vector型でなければならないのにRotation型になっている
       PRIM_COLOR, ALL_SIDES, <1, 0, 0>,1.0
      ]);
   }
 }
 
 エラーメッセージ:
 llSetPrimitiveParams error running rule #1: non-vector rule.


上記の例では、PRIM_SIZEを指定するパラメータとして、Vector型でなければいけないところを間違ってRotation型にしてしまっています。

X error running rule #Y: unknown rule

上のエラーと似ていますが、こちらは型の間違いではなく要素の順番を間違ったり、余計な要素が混ざっていると出ることがあります。
Xにはエラーとなった関数の名前(llSetPrimitiveParams,llParticleSystem等)が表示されます。
Yは引数のリストのうち、Y番目の要素が不正であることを意味します。

 default {
   state_entry(){
     llSetPrimitiveParams([
       PRIM_SIZE, <1, 1, 1>, <0, 0, 1>, ← 余分なVector型がある
       PRIM_COLOR, ALL_SIDES, <1, 0, 0>,1.0
      ]);
   }
 }
 
 エラーメッセージ:
 llSetPrimitiveParams error running rule #2: unknown rule


PRIM_SIZEを指定するパラメータとして、Vector型を一つ指定するべきところを、消し忘れたのか、2つ書いてあるためにエラーになります。

ビルドエラー

厳密に言えばLSLに関わるエラーではありませんが、製作中に遭遇するエラーとして挙げておきます。

The object may be out of range or may have been deleted.

スクリプト編集を行っているにも関わらず、そのスクリプトが入っているオブジェクトをワールド上から削除してしまったりした場合に起こります。
編集したスクリプトを保存しようとすると、
「オブジェクトがないので保存できないよ!」
というエラーになります。

タイマーでllDieするオブジェクトや、temprezオブジェクトを扱っているときにしばしば遭遇します。

この場合、インベントリの中に新たにスクリプトを作成し、編集したスクリプトコードを丸々コピー&ペーストして保存するか、オブジェクトをrezし、改めてスクリプト編集ウインドウを開いて、そこにペーストして保存する必要があります。
保存しないでスクリプト編集ウインドウを閉じた場合、編集結果は失われますので注意しましょう。

また、オブジェクトが存在するにも関わらずこのエラーが出る場合、アバターがオブジェクトから離れすぎていることが考えられます。
オブジェクトの近くまで移動するか、あるいはオブジェクトのほうをアバターの近くに移動させてから保存して下さい。

移動するオブジェクトの作成中に、思わぬ遠方にオブジェクトがすっ飛んでいってしまったときなどによく起こるエラーです。

Can't enable physics for objects that interpenetrate others

オブジェクトの編集中、物理属性を付与しようとしたときに、すでに他のオブジェクトや地面とぶつかっているとこのエラーが起きます。
何もない空間にオブジェクトを移動させた上で物理属性を付与すればOKです。

Pieces too far apart

オブジェクトをリンクさせようとしたとき、オブジェクト同士の距離が離れすぎていると起こります。
オブジェクト同士を近づけてリンクしましょう。
リンク後、個別にprimの位置を動かすことで、prim間の距離をあけることが出来ます。

またはリンクしようとしているprimを巨大化させるのも解決方法の一つです。
大きな状態でリンクし、その後個別にprimのサイズを元の大きさに戻します。



同じカテゴリー(lsl(スクリプト))の記事画像
河童工房再び
同じカテゴリー(lsl(スクリプト))の記事
 LSL-BBS (2007-11-19 12:13)
 河童工房再び (2007-11-07 22:44)
 LC2007 予告編 その2 (2007-10-17 18:00)
 LC2007 予告編 その1 (2007-10-12 18:00)
 LSL Convention Japan 2007 (2007-10-10 18:34)
 セカンドライフで作る リンデンスクリプト入門 (2007-07-10 13:46)
この記事へのコメント
フジ高島彩ニャンニャン流出DVD映像!


ゆず北川悠仁と交際しているフジアナウンサー高島彩。関係者によると年内結婚がウワサされているが・・・


http://kolmiyhtu.blogspot.com/




ネットではここ最近、以前流出したハ×撮り映像が話題となっている。



http://kolmiyhtu.blogspot.com/




結婚に影響が出なければ良いが・・・
Posted by ◆高島彩 年内結婚?!ネットで話題の流出DVD映像! at 2008年11月07日 12:42
人の潮流のファッション的な腕時計用意して全面的にビ.nciufhesiur@vjfjrtg.com
http://www.bagcopyweb.com/products_1.html
http://www.bagcopyweb.com/products_60.html
http://www.bagcopyweb.com/products_3.html
http://movb168.insanejournal.com/1329.html
Posted by fdgdrg at 2012年04月10日 16:38
価値を収集し自身もきびしく検査しますこれらの腕時計の真偽を確保できる質屋と品質の近道になる宝を洗いまなければならないnvdfiugjrh@mijcxcei.com。
http://www.selling-watch.comコピー腕時計
http://www.selling-watch.comロレックス偽物時計
http://www.selling-watch.comレプリカ時計
http://www.realtown.com/movb168/blog
Posted by dfsd at 2012年04月10日 16:38
潜在力はとても巨大bnhnk@gjrtgnh.com、時計の段の時期に内の値上がりする上昇する空間依然として存在する.
http://www.watchice.com/
http://www.watchlongines.com/
http://www.watchnote.com/
http://se528315.sweetcircles.com/
Posted by fserf at 2012年04月10日 16:39
通販

店舗HP: www.hihqbags.com

当社は激安 のA級S級N級品 激安通販専門店。

他社より、当社の商品は価格が一番安いのです。

当社の商品は香港で作って、高品質です。

晴子
Posted by 通販 at 2012年08月01日 16:43
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。