boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
author Jaroslav Tulach <jtulach@netbeans.org>
Mon, 15 Feb 2016 05:23:17 +0100
changeset 1051 d0e6c8f97dc3
parent 1019 e66e962fed13
child 1087 7865ad598184
permissions -rw-r--r--
Returning primitive array should yield ClassCastException during runtime
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    31  *
    32  * If you wish your version of this file to be governed by only the CDDL
    33  * or only the GPL Version 2, indicate your decision by adding
    34  * "[Contributor] elects to include this software in this distribution
    35  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    36  * single choice of license, a recipient has the option to distribute
    37  * your version of this file under either the CDDL, the GPL Version 2 or
    38  * to extend the choice of license to its licensees as provided above.
    39  * However, if you add GPL Version 2 code and therefore, elected the GPL
    40  * Version 2 license, then the option applies only if the new code is
    41  * made subject to such option by the copyright holder.
    42  */
    43 package org.netbeans.html.boot.impl;
    44 
    45 import java.io.IOException;
    46 import java.io.InputStream;
    47 import java.io.InputStreamReader;
    48 import java.io.Reader;
    49 import java.net.URL;
    50 import java.util.ArrayList;
    51 import java.util.Collections;
    52 import java.util.Enumeration;
    53 import java.util.List;
    54 import net.java.html.js.JavaScriptBody;
    55 import net.java.html.js.JavaScriptResource;
    56 import org.netbeans.html.boot.spi.Fn;
    57 import org.objectweb.asm.AnnotationVisitor;
    58 import org.objectweb.asm.ClassReader;
    59 import org.objectweb.asm.ClassVisitor;
    60 import org.objectweb.asm.ClassWriter;
    61 import org.objectweb.asm.FieldVisitor;
    62 import org.objectweb.asm.Label;
    63 import org.objectweb.asm.MethodVisitor;
    64 import org.objectweb.asm.Opcodes;
    65 import org.objectweb.asm.Type;
    66 import org.objectweb.asm.signature.SignatureReader;
    67 import org.objectweb.asm.signature.SignatureVisitor;
    68 import org.objectweb.asm.signature.SignatureWriter;
    69 
    70 /** Utilities related to bytecode transformations. Depend on asm.jar which
    71  * needs to be added to be provided to classpath to make methods in this 
    72  * class useful.
    73  *
    74  * @author Jaroslav Tulach
    75  */
    76 public final class FnUtils {
    77     
    78     private FnUtils() {
    79     }
    80     
    81     /** Seeks for {@link JavaScriptBody} and {@link JavaScriptResource} annotations
    82      * in the bytecode and converts them into real code. Used by Maven plugin
    83      * postprocessing classes.
    84      * 
    85      * @param bytecode the original bytecode with javascript specific annotations
    86      * @param loader the loader to load resources (scripts and classes) when needed
    87      * @return the transformed bytecode
    88      * @since 0.7
    89      */
    90     public static byte[] transform(byte[] bytecode, ClassLoader loader) {
    91         ClassReader cr = new ClassReader(bytecode) {
    92             // to allow us to compile with -profile compact1 on 
    93             // JDK8 while processing the class as JDK7, the highest
    94             // class format asm 4.1 understands to
    95             @Override
    96             public short readShort(int index) {
    97                 short s = super.readShort(index);
    98                 if (index == 6 && s > Opcodes.V1_7) {
    99                     return Opcodes.V1_7;
   100                 }
   101                 return s;
   102             }
   103         };
   104         FindInClass tst = new FindInClass(loader, null);
   105         cr.accept(tst, 0);
   106         if (tst.found > 0) {
   107             ClassWriter w = new ClassWriterEx(loader, cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
   108             FindInClass fic = new FindInClass(loader, w);
   109             cr.accept(fic, 0);
   110             bytecode = w.toByteArray();
   111         }
   112         return bytecode;
   113     }
   114     
   115     public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
   116         return new JsClassLoaderImpl(parent, f, d);
   117     }
   118 
   119     static String callback(final String body) {
   120         return new JsCallback() {
   121             @Override
   122             protected CharSequence callMethod(
   123                 String ident, String fqn, String method, String params
   124             ) {
   125                 StringBuilder sb = new StringBuilder();
   126                 if (ident != null) {
   127                     sb.append("vm.raw$");
   128                 } else {
   129                     sb.append("vm.");
   130                 }
   131                 sb.append(mangle(fqn, method, params));
   132                 sb.append("(");
   133                 if (ident != null) {
   134                     sb.append(ident);
   135                 }
   136                 return sb;
   137             }
   138 
   139         }.parse(body);
   140     }
   141 
   142     private static final class FindInClass extends ClassVisitor {
   143         private String name;
   144         private int found;
   145         private String resource;
   146 
   147         public FindInClass(ClassLoader l, ClassVisitor cv) {
   148             super(Opcodes.ASM4, cv);
   149         }
   150 
   151         @Override
   152         public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
   153             this.name = name;
   154             super.visit(version, access, name, signature, superName, interfaces);
   155         }
   156 
   157         @Override
   158         public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
   159             final AnnotationVisitor del = super.visitAnnotation(desc, visible);
   160             if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
   161                 return new LoadResource(del);
   162             }
   163             return del;
   164         }
   165 
   166         @Override
   167         public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
   168             return new FindInMethod(access, name, desc,
   169                     super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
   170             );
   171         }
   172 
   173         @Override
   174         public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
   175             if (name.startsWith("$$fn$$")) {
   176                 return null;
   177             }
   178             return superField(access, name, desc, signature, value);
   179         }
   180 
   181         final FieldVisitor superField(int access, String name, String desc, String signature, Object value) {
   182             return super.visitField(access, name, desc, signature, value);
   183         }
   184 
   185         private final class FindInMethod extends MethodVisitor {
   186 
   187             private final String name;
   188             private final String desc;
   189             private final int access;
   190             private FindInAnno fia;
   191             private boolean bodyGenerated;
   192 
   193             public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
   194                 super(Opcodes.ASM4, mv);
   195                 this.access = access;
   196                 this.name = name;
   197                 this.desc = desc;
   198             }
   199 
   200             @Override
   201             public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
   202                 if ("Lnet/java/html/js/JavaScriptBody;".equals(desc)) { // NOI18N
   203                     found++;
   204                     return new FindInAnno();
   205                 }
   206                 return super.visitAnnotation(desc, visible);
   207             }
   208 
   209             private void generateJSBody(FindInAnno fia) {
   210                 this.fia = fia;
   211             }
   212 
   213             @Override
   214             public void visitCode() {
   215                 if (fia == null) {
   216                     return;
   217                 }
   218                 generateBody(true);
   219             }
   220 
   221             private boolean generateBody(boolean hasCode) {
   222                 if (bodyGenerated) {
   223                     return false;
   224                 }
   225                 bodyGenerated = true;
   226                 if (mv != null) {
   227                     AnnotationVisitor va = super.visitAnnotation("Lnet/java/html/js/JavaScriptBody;", false);
   228                     AnnotationVisitor varr = va.visitArray("args");
   229                     for (String argName : fia.args) {
   230                         varr.visit(null, argName);
   231                     }
   232                     varr.visitEnd();
   233                     va.visit("javacall", fia.javacall);
   234                     va.visit("body", fia.body);
   235                     va.visitEnd();
   236                 }
   237                 
   238                 String body;
   239                 List<String> args;
   240                 if (fia.javacall) {
   241                     body = callback(fia.body);
   242                     args = new ArrayList<String>(fia.args);
   243                     args.add("vm");
   244                 } else {
   245                     body = fia.body;
   246                     args = fia.args;
   247                 }
   248 
   249                 super.visitFieldInsn(
   250                         Opcodes.GETSTATIC, FindInClass.this.name,
   251                         "$$fn$$" + name + "_" + found,
   252                         "Lorg/netbeans/html/boot/spi/Fn;"
   253                 );
   254                 super.visitInsn(Opcodes.DUP);
   255                 super.visitMethodInsn(
   256                         Opcodes.INVOKESTATIC,
   257                         "org/netbeans/html/boot/spi/Fn", "isValid",
   258                         "(Lorg/netbeans/html/boot/spi/Fn;)Z"
   259                 );
   260                 Label ifNotNull = new Label();
   261                 super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
   262 
   263                 // init Fn
   264                 super.visitInsn(Opcodes.POP);
   265                 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
   266                 super.visitInsn(fia.keepAlive ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
   267                 super.visitLdcInsn(body);
   268                 super.visitIntInsn(Opcodes.SIPUSH, args.size());
   269                 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
   270                 boolean needsVM = false;
   271                 for (int i = 0; i < args.size(); i++) {
   272                     assert !needsVM;
   273                     String argName = args.get(i);
   274                     needsVM = "vm".equals(argName);
   275                     super.visitInsn(Opcodes.DUP);
   276                     super.visitIntInsn(Opcodes.BIPUSH, i);
   277                     super.visitLdcInsn(argName);
   278                     super.visitInsn(Opcodes.AASTORE);
   279                 }
   280                 super.visitMethodInsn(Opcodes.INVOKESTATIC,
   281                         "org/netbeans/html/boot/spi/Fn", "define",
   282                         "(Ljava/lang/Class;ZLjava/lang/String;[Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
   283                 );
   284                 Label noPresenter = new Label();
   285                 super.visitInsn(Opcodes.DUP);
   286                 super.visitJumpInsn(Opcodes.IFNULL, noPresenter);
   287                 if (resource != null) {
   288                     super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
   289                     super.visitLdcInsn(resource);
   290                     super.visitMethodInsn(Opcodes.INVOKESTATIC,
   291                             "org/netbeans/html/boot/spi/Fn", "preload",
   292                             "(Lorg/netbeans/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
   293                     );
   294                 }
   295                 super.visitInsn(Opcodes.DUP);
   296                 super.visitFieldInsn(
   297                         Opcodes.PUTSTATIC, FindInClass.this.name,
   298                         "$$fn$$" + name + "_" + found,
   299                         "Lorg/netbeans/html/boot/spi/Fn;"
   300                 );
   301                 // end of Fn init
   302 
   303                 super.visitLabel(ifNotNull);
   304 
   305                 final int offset;
   306                 if ((access & Opcodes.ACC_STATIC) == 0) {
   307                     offset = 1;
   308                     super.visitIntInsn(Opcodes.ALOAD, 0);
   309                 } else {
   310                     offset = 0;
   311                     super.visitInsn(Opcodes.ACONST_NULL);
   312                 }
   313 
   314                 super.visitIntInsn(Opcodes.SIPUSH, args.size());
   315                 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
   316 
   317                 class SV extends SignatureVisitor {
   318 
   319                     private boolean nowReturn;
   320                     private Type returnType;
   321                     private int index;
   322                     private int loadIndex = offset;
   323 
   324                     public SV() {
   325                         super(Opcodes.ASM4);
   326                     }
   327 
   328                     @Override
   329                     public void visitBaseType(char descriptor) {
   330                         final Type t = Type.getType("" + descriptor);
   331                         if (nowReturn) {
   332                             returnType = t;
   333                             return;
   334                         }
   335                         FindInMethod.super.visitInsn(Opcodes.DUP);
   336                         FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
   337                         FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
   338                         String factory;
   339                         switch (descriptor) {
   340                             case 'I':
   341                                 factory = "java/lang/Integer";
   342                                 break;
   343                             case 'J':
   344                                 factory = "java/lang/Long";
   345                                 loadIndex++;
   346                                 break;
   347                             case 'S':
   348                                 factory = "java/lang/Short";
   349                                 break;
   350                             case 'F':
   351                                 factory = "java/lang/Float";
   352                                 break;
   353                             case 'D':
   354                                 factory = "java/lang/Double";
   355                                 loadIndex++;
   356                                 break;
   357                             case 'Z':
   358                                 factory = "java/lang/Boolean";
   359                                 break;
   360                             case 'C':
   361                                 factory = "java/lang/Character";
   362                                 break;
   363                             case 'B':
   364                                 factory = "java/lang/Byte";
   365                                 break;
   366                             default:
   367                                 throw new IllegalStateException(t.toString());
   368                         }
   369                         FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
   370                                 factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
   371                         );
   372                         FindInMethod.super.visitInsn(Opcodes.AASTORE);
   373                     }
   374 
   375                     @Override
   376                     public SignatureVisitor visitArrayType() {
   377                         if (nowReturn) {
   378                             return new SignatureVisitor(Opcodes.ASM4) {
   379                                 @Override
   380                                 public void visitClassType(String name) {
   381                                     returnType = Type.getType("[" + Type.getObjectType(name).getDescriptor());
   382                                 }
   383 
   384                                 @Override
   385                                 public void visitBaseType(char descriptor) {
   386                                     returnType = Type.getType("[" + descriptor);
   387                                 }
   388                             };
   389                         }
   390                         loadObject();
   391                         return new SignatureWriter();
   392                     }
   393 
   394                     @Override
   395                     public void visitClassType(String name) {
   396                         if (nowReturn) {
   397                             returnType = Type.getObjectType(name);
   398                             return;
   399                         }
   400                         loadObject();
   401                     }
   402 
   403                     @Override
   404                     public SignatureVisitor visitReturnType() {
   405                         nowReturn = true;
   406                         return this;
   407                     }
   408 
   409                     private void loadObject() {
   410                         FindInMethod.super.visitInsn(Opcodes.DUP);
   411                         FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
   412                         FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
   413                         FindInMethod.super.visitInsn(Opcodes.AASTORE);
   414                     }
   415 
   416                 }
   417                 SV sv = new SV();
   418                 SignatureReader sr = new SignatureReader(desc);
   419                 sr.accept(sv);
   420 
   421                 if (needsVM) {
   422                     FindInMethod.super.visitInsn(Opcodes.DUP);
   423                     FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
   424                     int lastSlash = FindInClass.this.name.lastIndexOf('/');
   425                     String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
   426                     FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
   427                     FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
   428                     FindInMethod.super.visitInsn(Opcodes.AASTORE);
   429                 }
   430 
   431                 if (fia.wait4js) {
   432                     super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
   433                             "org/netbeans/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
   434                     );
   435                     switch (sv.returnType.getSort()) {
   436                         case Type.VOID:
   437                             super.visitInsn(Opcodes.RETURN);
   438                             break;
   439                         case Type.ARRAY:
   440                         case Type.OBJECT:
   441                             super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
   442                             super.visitInsn(Opcodes.ARETURN);
   443                             break;
   444                         case Type.BOOLEAN:
   445                             super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
   446                             super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
   447                                     "java/lang/Boolean", "booleanValue", "()Z"
   448                             );
   449                             super.visitInsn(Opcodes.IRETURN);
   450                             break;
   451                         default:
   452                             super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
   453                             super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
   454                                     "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
   455                             );
   456                             super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
   457                     }
   458                 } else {
   459                     super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
   460                             "org/netbeans/html/boot/spi/Fn", "invokeLater", "(Ljava/lang/Object;[Ljava/lang/Object;)V"
   461                     );
   462                     super.visitInsn(Opcodes.RETURN);
   463                 }
   464                 super.visitLabel(noPresenter);
   465                 if (hasCode) {
   466                     super.visitCode();
   467                 } else {
   468                     super.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException");
   469                     super.visitInsn(Opcodes.DUP);
   470                     super.visitLdcInsn("No presenter active. Use BrwsrCtx.execute!");
   471                     super.visitMethodInsn(Opcodes.INVOKESPECIAL, 
   472                         "java/lang/IllegalStateException", "<init>", "(Ljava/lang/String;)V"
   473                     );
   474                     this.visitInsn(Opcodes.ATHROW);
   475                 }
   476                 return true;
   477             }
   478             
   479             @Override
   480             public void visitEnd() {
   481                 super.visitEnd();
   482                 if (fia != null) {
   483                     if (generateBody(false)) {
   484                         // native method
   485                         super.visitMaxs(1, 0);
   486                     }
   487                     FindInClass.this.superField(
   488                             Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
   489                             "$$fn$$" + name + "_" + found,
   490                             "Lorg/netbeans/html/boot/spi/Fn;",
   491                             null, null
   492                     );
   493                 }
   494             }
   495 
   496             private final class FindInAnno extends AnnotationVisitor {
   497 
   498                 List<String> args = new ArrayList<String>();
   499                 String body;
   500                 boolean javacall = false;
   501                 boolean wait4js = true;
   502                 boolean keepAlive = true;
   503 
   504                 public FindInAnno() {
   505                     super(Opcodes.ASM4);
   506                 }
   507 
   508                 @Override
   509                 public void visit(String name, Object value) {
   510                     if (name == null) {
   511                         args.add((String) value);
   512                         return;
   513                     }
   514                     if (name.equals("javacall")) { // NOI18N
   515                         javacall = (Boolean) value;
   516                         return;
   517                     }
   518                     if (name.equals("wait4js")) { // NOI18N
   519                         wait4js = (Boolean) value;
   520                         return;
   521                     }
   522                     if (name.equals("keepAlive")) { // NOI18N
   523                         keepAlive = (Boolean) value;
   524                         return;
   525                     }
   526                     assert name.equals("body"); // NOI18N
   527                     body = (String) value;
   528                 }
   529 
   530                 @Override
   531                 public AnnotationVisitor visitArray(String name) {
   532                     return this;
   533                 }
   534 
   535                 @Override
   536                 public void visitEnd() {
   537                     if (body != null) {
   538                         generateJSBody(this);
   539                     }
   540                 }
   541             }
   542         }
   543 
   544         private final class LoadResource extends AnnotationVisitor {
   545             public LoadResource(AnnotationVisitor av) {
   546                 super(Opcodes.ASM4, av);
   547             }
   548 
   549             @Override
   550             public void visit(String attrName, Object value) {
   551                 super.visit(attrName, value);
   552                 String relPath = (String) value;
   553                 if (relPath.startsWith("/")) {
   554                     resource = relPath;
   555                 } else {
   556                     int last = name.lastIndexOf('/');
   557                     String fullPath = name.substring(0, last + 1) + relPath;
   558                     resource = fullPath;
   559                 }
   560             }
   561         }
   562     }
   563 
   564     private static class ClassWriterEx extends ClassWriter {
   565 
   566         private final ClassLoader loader;
   567 
   568         public ClassWriterEx(ClassLoader l, ClassReader classReader, int flags) {
   569             super(classReader, flags);
   570             this.loader = l;
   571         }
   572 
   573         @Override
   574         protected String getCommonSuperClass(final String type1, final String type2) {
   575             Class<?> c, d;
   576             try {
   577                 c = Class.forName(type1.replace('/', '.'), false, loader);
   578                 d = Class.forName(type2.replace('/', '.'), false, loader);
   579             } catch (Exception e) {
   580                 throw new RuntimeException(e.toString());
   581             }
   582             if (c.isAssignableFrom(d)) {
   583                 return type1;
   584             }
   585             if (d.isAssignableFrom(c)) {
   586                 return type2;
   587             }
   588             if (c.isInterface() || d.isInterface()) {
   589                 return "java/lang/Object";
   590             } else {
   591                 do {
   592                     c = c.getSuperclass();
   593                 } while (!c.isAssignableFrom(d));
   594                 return c.getName().replace('.', '/');
   595             }
   596         }
   597     }
   598 
   599     static class JsClassLoaderImpl extends JsClassLoader {
   600 
   601         private final FindResources f;
   602         private final Fn.Presenter d;
   603 
   604         public JsClassLoaderImpl(ClassLoader parent, FindResources f, Fn.Presenter d) {
   605             super(parent);
   606             setDefaultAssertionStatus(JsClassLoader.class.desiredAssertionStatus());
   607             this.f = f;
   608             this.d = d;
   609         }
   610 
   611         @Override
   612         protected URL findResource(String name) {
   613             List<URL> l = res(name, true);
   614             return l.isEmpty() ? null : l.get(0);
   615         }
   616 
   617         @Override
   618         protected Enumeration<URL> findResources(String name) {
   619             return Collections.enumeration(res(name, false));
   620         }
   621         
   622         private List<URL> res(String name, boolean oneIsEnough) {
   623             List<URL> l = new ArrayList<URL>();
   624             f.findResources(name, l, oneIsEnough);
   625             return l;
   626         }
   627     
   628         @Override
   629         protected Class<?> findClass(String name) throws ClassNotFoundException {
   630             if (name.startsWith("javafx")) {
   631                 return Class.forName(name);
   632             }
   633             if (name.startsWith("netscape")) {
   634                 return Class.forName(name);
   635             }
   636             if (name.startsWith("com.sun")) {
   637                 return Class.forName(name);
   638             }
   639             if (name.startsWith("org.netbeans.html.context.spi")) {
   640                 return Class.forName(name);
   641             }
   642             if (name.startsWith("net.java.html.BrwsrCtx")) {
   643                 return Class.forName(name);
   644             }
   645             if (name.equals(JsClassLoader.class.getName())) {
   646                 return JsClassLoader.class;
   647             }
   648             if (name.equals(Fn.class.getName())) {
   649                 return Fn.class;
   650             }
   651             if (name.equals(Fn.Presenter.class.getName())) {
   652                 return Fn.Presenter.class;
   653             }
   654             if (name.equals(Fn.ToJavaScript.class.getName())) {
   655                 return Fn.ToJavaScript.class;
   656             }
   657             if (name.equals(Fn.FromJavaScript.class.getName())) {
   658                 return Fn.FromJavaScript.class;
   659             }
   660             if (name.equals(FnUtils.class.getName())) {
   661                 return FnUtils.class;
   662             }
   663             if (
   664                 name.equals("org.netbeans.html.boot.spi.Fn") ||
   665                 name.equals("org.netbeans.html.boot.impl.FnUtils") ||
   666                 name.equals("org.netbeans.html.boot.impl.FnContext")
   667             ) {
   668                 return Class.forName(name);
   669             }
   670             URL u = findResource(name.replace('.', '/') + ".class");
   671             if (u != null) {
   672                 InputStream is = null;
   673                 try {
   674                     is = u.openStream();
   675                     byte[] arr = new byte[is.available()];
   676                     int len = 0;
   677                     while (len < arr.length) {
   678                         int read = is.read(arr, len, arr.length - len);
   679                         if (read == -1) {
   680                             throw new IOException("Can't read " + u);
   681                         }
   682                         len += read;
   683                     }
   684                     is.close();
   685                     is = null;
   686                     if (JsPkgCache.process(this, name)) {
   687                         arr = FnUtils.transform(arr, this);
   688                     }
   689                     return defineClass(name, arr, 0, arr.length);
   690                 } catch (IOException ex) {
   691                     throw new ClassNotFoundException("Can't load " + name, ex);
   692                 } finally {
   693                     try {
   694                         if (is != null) is.close();
   695                     } catch (IOException ex) {
   696                         throw new ClassNotFoundException(null, ex);
   697                     }
   698                 }
   699             }
   700             return super.findClass(name);
   701         }
   702     
   703         protected Fn defineFn(String code, String... names) {
   704             return d.defineFn(code, names);
   705         }
   706         
   707         protected void loadScript(Reader code) throws Exception {
   708             d.loadScript(code);
   709         }
   710     }
   711 }