/* * Copyright 2013 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.gwt.core.ext.soyc.coderef; import com.google.gwt.core.ext.linker.EmittedArtifact.Visibility; import com.google.gwt.core.ext.linker.SyntheticArtifact; import com.google.gwt.core.ext.soyc.SourceMapRecorder; import com.google.gwt.core.ext.soyc.coderef.EntityDescriptor.Fragment; import com.google.gwt.core.linker.SoycReportLinker; import com.google.gwt.dev.jjs.InternalCompilerException; import com.google.gwt.dev.jjs.JsSourceMap; import com.google.gwt.dev.jjs.ast.JClassType; import com.google.gwt.dev.jjs.ast.JDeclaredType; import com.google.gwt.dev.jjs.ast.JField; import com.google.gwt.dev.jjs.ast.JMethod; import com.google.gwt.dev.jjs.ast.JProgram; import com.google.gwt.dev.jjs.ast.JRunAsync; import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap; import com.google.gwt.dev.jjs.impl.codesplitter.FragmentPartitioningResult; import com.google.gwt.dev.js.SizeBreakdown; import com.google.gwt.dev.js.ast.JsName; import com.google.gwt.thirdparty.guava.common.collect.Lists; import com.google.gwt.thirdparty.guava.common.collect.Maps; import com.google.gwt.thirdparty.guava.common.collect.Sets; import com.google.gwt.thirdparty.json.JSONArray; import com.google.gwt.thirdparty.json.JSONException; import com.google.gwt.thirdparty.json.JSONObject; import com.google.gwt.util.tools.Utility; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * Creates the entities artifacts for the new soyc. */ public class EntityRecorder { public static final String ENTITIES = "entities"; public static final String FRAGMENT_ID = "id"; public static final String FRAGMENT_SIZE = "size"; public static final String FRAGMENT_STR_VAR = "strAndVarSize"; public static final String FRAGMENT_POINTS = "runAsyncs"; public static final String FRAGMENTS = "fragments"; public static final String INITIAL_SEQUENCE = "initialSequence"; public static List<SyntheticArtifact> makeSoycArtifacts(int permutationId, List<JsSourceMap> sourceInfoMaps, String sourceMapFilePrefix, JavaToJavaScriptMap jjsmap, SizeBreakdown[] sizeBreakdowns, DependencyGraphRecorder codeGraph, JProgram jprogram) { List<SyntheticArtifact> artifacts = Lists.newArrayList(); try { EntityRecorder recorder = new EntityRecorder(sizeBreakdowns, permutationId); recorder.recordCodeReferences(codeGraph, sizeBreakdowns, jjsmap); recorder.recordFragments(jprogram); artifacts.addAll(recorder.toReturn); artifacts.addAll(SourceMapRecorder.execWithJavaNames(permutationId, sourceInfoMaps, sourceMapFilePrefix)); } catch (Exception e) { throw new InternalCompilerException(e.toString(), e); } return artifacts; } private final List<SyntheticArtifact> toReturn = Lists.newArrayList(); private final int permutationId; private final int[] fragmentSizes; // String and variable sizes private final int[] otherSizes; private JSONObject[] sizeMetrics; private EntityRecorder(SizeBreakdown[] sizeBreakdowns, int permutationId) { fragmentSizes = new int[sizeBreakdowns.length]; otherSizes = new int[sizeBreakdowns.length]; for (int i = 0; i < sizeBreakdowns.length; i++) { fragmentSizes[i] = sizeBreakdowns[i].getSize(); otherSizes[i] = 0; } this.permutationId = permutationId; } private JSONObject getSizeMetrics(int fragment) throws JSONException { JSONObject obj = new JSONObject(); obj.put(FRAGMENT_ID, fragment); obj.put(FRAGMENT_SIZE, this.fragmentSizes[fragment]); obj.put(FRAGMENT_STR_VAR, this.otherSizes[fragment]); return obj; } private void recordCodeReferences(DependencyGraphRecorder codeGraph, SizeBreakdown[] sizeBreakdowns, JavaToJavaScriptMap jjsmap) throws IOException, JSONException { this.sizeMetrics = new JSONObject[sizeBreakdowns.length]; // add sizes and other info for (int i = 0; i < sizeBreakdowns.length; i++) { for (Entry<JsName, Integer> kv : sizeBreakdowns[i].getSizeMap().entrySet()) { JsName name = kv.getKey(); int size = kv.getValue(); // find method JMethod method = jjsmap.nameToMethod(name); if (method != null) { codeGraph.methodDescriptorFrom(method).addFragment(new Fragment(i, size)); continue; } // find field JField field = jjsmap.nameToField(name); if ((field != null) && (field.getEnclosingType() != null)) { codeGraph.classDescriptorFrom(field.getEnclosingType()).fieldFrom(field) .addFragment(new Fragment(i, size)); continue; } // find class JClassType type = jjsmap.nameToType(name); if (type != null) { codeGraph.classDescriptorFrom(type).addFragment(new Fragment(i, size)); continue; } // otherwise is a string or variable this.otherSizes[i] += size; } sizeMetrics[i] = this.getSizeMetrics(i); } // adding symbol names considering that jjsmap has all obfuscated name for all entities for (ClassDescriptor cls : codeGraph.getCodeModel().values()) { JDeclaredType type = cls.getTypeReference(); if (type instanceof JClassType) { JsName jsName = jjsmap.nameForType((JClassType) type); if (jsName != null) { cls.addObfuscatedName(jsName.getShortIdent()); } } for (MethodDescriptor mth : cls.getMethods()) { for (JMethod jMethod : mth.getMethodReferences()) { JsName jsName = jjsmap.nameForMethod(jMethod); if (jsName != null) { mth.addObfuscatedName(jsName.getShortIdent()); } } } for (JField field : type.getFields()) { JsName jsName = jjsmap.nameForField(field); if (jsName != null) { cls.fieldFrom(field).addObfuscatedName(jsName.getShortIdent()); } } } // build json addArtifactFromJson( EntityDescriptorJsonTranslator.writeJson( PackageDescriptor.from(codeGraph.getCodeModel())), ENTITIES + permutationId + ".json"); } private String addArtifactFromJson(Object value, String named) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintWriter writer = new PrintWriter(baos); writer.write(value.toString()); Utility.close(writer); // TODO(ocallau) Must be updated with the correct/final linker SyntheticArtifact artifact = new SyntheticArtifact( SoycReportLinker.class, named, baos.toByteArray()); artifact.setVisibility(Visibility.LegacyDeploy); toReturn.add(artifact); return named; } private void recordFragments(JProgram jprogram) throws IOException, JSONException { JSONObject jsonPoints = new JSONObject(); // adding runAsyncs, if any FragmentPartitioningResult partitionResult = jprogram.getFragmentPartitioningResult(); // a FragmentPartitioningResult' instance exist if and only if there are runAyncs nodes if (partitionResult != null) { Map<Integer, Set<String>> runAsyncPerFragment = Maps.newHashMap(); for (JRunAsync runAsync : jprogram.getRunAsyncs()) { int fragmentId = partitionResult.getFragmentForRunAsync(runAsync.getRunAsyncId()); Set<String> runAsyncNames = runAsyncPerFragment.get(fragmentId); if (runAsyncNames == null) { runAsyncNames = Sets.newHashSet(); } runAsyncNames.add(runAsync.getName()); runAsyncPerFragment.put(fragmentId, runAsyncNames); } for (Integer idx : runAsyncPerFragment.keySet()) { sizeMetrics[idx].put(FRAGMENT_POINTS, runAsyncPerFragment.get(idx)); } // initial fragment sequence points JSONArray initialSequence = new JSONArray(); for (int fragId : jprogram.getInitialFragmentIdSequence()) { initialSequence.put(partitionResult.getFragmentForRunAsync(fragId)); } jsonPoints.put(INITIAL_SEQUENCE, initialSequence); } jsonPoints.put(FRAGMENTS, this.sizeMetrics); addArtifactFromJson(jsonPoints, FRAGMENTS + permutationId + ".json"); } }